cuelo 0.10.2 → 0.10.4

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 (135) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +15 -15
  3. package/.next/build-manifest.json +2 -2
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/required-server-files.js +1 -1
  6. package/.next/required-server-files.json +1 -1
  7. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  17. package/.next/server/app/_not-found.html +1 -1
  18. package/.next/server/app/_not-found.rsc +2 -2
  19. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  21. package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  22. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  23. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  24. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  25. package/.next/server/app/api/agent/[id]/account/route.js +1 -1
  26. package/.next/server/app/api/agent/[id]/events/route.js +1 -1
  27. package/.next/server/app/api/agent/[id]/route.js +1 -1
  28. package/.next/server/app/api/agent/new/route.js +1 -1
  29. package/.next/server/app/api/agent/running/route.js +1 -1
  30. package/.next/server/app/api/cwd/validate/route.js +1 -1
  31. package/.next/server/app/api/default-cwd/route.js +1 -1
  32. package/.next/server/app/api/display-settings/route.js +1 -1
  33. package/.next/server/app/api/file-index/route.js +1 -1
  34. package/.next/server/app/api/files/[...path]/route.js +1 -1
  35. package/.next/server/app/api/git/diff/route.js +2 -2
  36. package/.next/server/app/api/git/status/route.js +2 -2
  37. package/.next/server/app/api/gpt6/handles/route.js +1 -1
  38. package/.next/server/app/api/gpt6/reply/route.js +1 -1
  39. package/.next/server/app/api/gpt6/token/route.js +1 -1
  40. package/.next/server/app/api/gpt6/web6-session/route.js +1 -1
  41. package/.next/server/app/api/live/call/[callId]/route.js +1 -1
  42. package/.next/server/app/api/live/character-voice/route.js +1 -1
  43. package/.next/server/app/api/live/events/route.js +1 -1
  44. package/.next/server/app/api/live/offer/route.js +1 -1
  45. package/.next/server/app/api/live/voices/route.js +1 -1
  46. package/.next/server/app/api/lounge/events/route.js +1 -1
  47. package/.next/server/app/api/lounge/route.js +1 -1
  48. package/.next/server/app/api/mcp/route.js +2 -2
  49. package/.next/server/app/api/model-roles/route.js +1 -1
  50. package/.next/server/app/api/models/route.js +1 -1
  51. package/.next/server/app/api/plugins/route.js +1 -1
  52. package/.next/server/app/api/project-trust/route.js +1 -1
  53. package/.next/server/app/api/projects/route.js +1 -1
  54. package/.next/server/app/api/sessions/[id]/auto-name/route.js +1 -1
  55. package/.next/server/app/api/sessions/[id]/context/route.js +1 -1
  56. package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +1 -1
  57. package/.next/server/app/api/sessions/[id]/export/route.js +1 -1
  58. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  59. package/.next/server/app/api/sessions/[id]/state/route.js +1 -1
  60. package/.next/server/app/api/sessions/route.js +1 -1
  61. package/.next/server/app/api/settings/route.js +1 -1
  62. package/.next/server/app/api/skills/check/route.js +1 -1
  63. package/.next/server/app/api/skills/install/route.js +1 -1
  64. package/.next/server/app/api/skills/route.js +1 -1
  65. package/.next/server/app/api/skills/update/route.js +1 -1
  66. package/.next/server/app/api/theme/route.js +1 -1
  67. package/.next/server/app/api/tool-image/route.js +1 -1
  68. package/.next/server/app/api/update-maintenance/route.js +1 -1
  69. package/.next/server/app/api/updates/route.js +1 -1
  70. package/.next/server/app/api/web6/consults/route.js +1 -1
  71. package/.next/server/app/api/worktrees/route.js +1 -1
  72. package/.next/server/app/index.html +1 -1
  73. package/.next/server/app/index.rsc +4 -4
  74. package/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
  75. package/.next/server/app/index.segments/_full.segment.rsc +4 -4
  76. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  77. package/.next/server/app/index.segments/_index.segment.rsc +2 -2
  78. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  79. package/.next/server/app/office/page_client-reference-manifest.js +1 -1
  80. package/.next/server/app/office-preview/page_client-reference-manifest.js +1 -1
  81. package/.next/server/app/office-preview.html +1 -1
  82. package/.next/server/app/office-preview.rsc +2 -2
  83. package/.next/server/app/office-preview.segments/_full.segment.rsc +2 -2
  84. package/.next/server/app/office-preview.segments/_head.segment.rsc +1 -1
  85. package/.next/server/app/office-preview.segments/_index.segment.rsc +2 -2
  86. package/.next/server/app/office-preview.segments/_tree.segment.rsc +1 -1
  87. package/.next/server/app/office-preview.segments/office-preview/__PAGE__.segment.rsc +1 -1
  88. package/.next/server/app/office-preview.segments/office-preview.segment.rsc +1 -1
  89. package/.next/server/app/office.html +1 -1
  90. package/.next/server/app/office.rsc +4 -4
  91. package/.next/server/app/office.segments/_full.segment.rsc +4 -4
  92. package/.next/server/app/office.segments/_head.segment.rsc +1 -1
  93. package/.next/server/app/office.segments/_index.segment.rsc +2 -2
  94. package/.next/server/app/office.segments/_tree.segment.rsc +1 -1
  95. package/.next/server/app/office.segments/office/__PAGE__.segment.rsc +3 -3
  96. package/.next/server/app/office.segments/office.segment.rsc +1 -1
  97. package/.next/server/app/page_client-reference-manifest.js +1 -1
  98. package/.next/server/app/recover/page_client-reference-manifest.js +1 -1
  99. package/.next/server/app/recover.html +1 -1
  100. package/.next/server/app/recover.rsc +2 -2
  101. package/.next/server/app/recover.segments/_full.segment.rsc +2 -2
  102. package/.next/server/app/recover.segments/_head.segment.rsc +1 -1
  103. package/.next/server/app/recover.segments/_index.segment.rsc +2 -2
  104. package/.next/server/app/recover.segments/_tree.segment.rsc +1 -1
  105. package/.next/server/app/recover.segments/recover/__PAGE__.segment.rsc +1 -1
  106. package/.next/server/app/recover.segments/recover.segment.rsc +1 -1
  107. package/.next/server/app-paths-manifest.json +15 -15
  108. package/.next/server/chunks/1008.js +3 -3
  109. package/.next/server/chunks/5489.js +2 -2
  110. package/.next/server/chunks/5582.js +2 -2
  111. package/.next/server/middleware-build-manifest.js +1 -1
  112. package/.next/server/pages/404.html +1 -1
  113. package/.next/server/pages/500.html +1 -1
  114. package/.next/server/server-reference-manifest.json +1 -1
  115. package/.next/static/chunks/{9575-9196474bb9e5c752.js → 9575-c7186831a3bfe17c.js} +2 -2
  116. package/.next/static/chunks/app/{layout-713017c65de14601.js → layout-bfc26328305a8fac.js} +1 -1
  117. package/.next/trace +108 -108
  118. package/.next/trace-build +1 -1
  119. package/Tools/CUELO_Setup/files/native-runtime-patch.js +1 -1
  120. package/Tools/OMP_Global_Config/agent/agents/maker.md +14 -11
  121. package/Tools/OMP_Global_Config/agent/extensions/character-voice.ts +1 -1
  122. package/Tools/OMP_Global_Config/agent/extensions/command-guard/index.ts +12 -4
  123. package/Tools/OMP_Global_Config/agent/extensions/korean-reply-guard.ts +2 -2
  124. package/Tools/OMP_Global_Config/agent/rules/harness-policy.json +2 -2
  125. package/Tools/OMP_Global_Config/agent/rules/meta-harness.md +2 -2
  126. package/Tools/OMP_Global_Config/agent/rules/subagent.md +5 -3
  127. package/Tools/OMP_Global_Config/agent/sop/_common.md +12 -9
  128. package/Tools/OMP_Global_Config/agent/sop/maker.md +1 -1
  129. package/Tools/OMP_Global_Config/agent/tools/git-finalizer/index.ts +7 -28
  130. package/Tools/OMP_Global_Config/agent/tools/git-finalizer/wsl-git.ts +96 -0
  131. package/Tools/OMP_Global_Config/agent/tools/windows-build/index.ts +330 -0
  132. package/Tools/OMP_Global_Config/patches/apply-core-patch.mjs +133 -0
  133. package/package.json +3 -2
  134. /package/.next/static/{S8tJkE71e2D0DgH4X1s0S → HCr7GGX3_oM-hKknNcPSW}/_buildManifest.js +0 -0
  135. /package/.next/static/{S8tJkE71e2D0DgH4X1s0S → HCr7GGX3_oM-hKknNcPSW}/_ssgManifest.js +0 -0
package/.next/trace-build CHANGED
@@ -1 +1 @@
1
- [{"name":"run-webpack","duration":114018786,"timestamp":687805,"id":14,"parentId":1,"tags":{},"startTime":1791507921870,"traceId":"6c75260e77b7370d"},{"name":"run-typescript","duration":24733849,"timestamp":115113546,"id":11084,"parentId":1,"tags":{},"startTime":1791508036296,"traceId":"6c75260e77b7370d"},{"name":"static-check","duration":4561356,"timestamp":139975975,"id":11087,"parentId":1,"tags":{},"startTime":1791508061159,"traceId":"6c75260e77b7370d"},{"name":"static-generation","duration":12317561,"timestamp":144649546,"id":11259,"parentId":1,"tags":{},"startTime":1791508065832,"traceId":"6c75260e77b7370d"},{"name":"collect-build-traces","duration":21852595,"timestamp":144608304,"id":11256,"parentId":1,"tags":{},"startTime":1791508065791,"traceId":"6c75260e77b7370d"},{"name":"telemetry-flush","duration":109,"timestamp":166472512,"id":11268,"parentId":1,"tags":{},"startTime":1791508087655,"traceId":"6c75260e77b7370d"},{"name":"next-build","duration":165897246,"timestamp":575399,"id":1,"tags":{"buildMode":"default","version":"16.2.12","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1791507921758,"traceId":"6c75260e77b7370d"}]
1
+ [{"name":"run-webpack","duration":80845889,"timestamp":575464,"id":14,"parentId":1,"tags":{},"startTime":1791525988965,"traceId":"87321b73e1bed0b6"},{"name":"run-typescript","duration":14113679,"timestamp":81770294,"id":11087,"parentId":1,"tags":{},"startTime":1791526070160,"traceId":"87321b73e1bed0b6"},{"name":"static-check","duration":13806050,"timestamp":96923925,"id":11090,"parentId":1,"tags":{},"startTime":1791526085314,"traceId":"87321b73e1bed0b6"},{"name":"static-generation","duration":7186106,"timestamp":110810204,"id":11262,"parentId":1,"tags":{},"startTime":1791526099200,"traceId":"87321b73e1bed0b6"},{"name":"collect-build-traces","duration":15963218,"timestamp":110789328,"id":11259,"parentId":1,"tags":{},"startTime":1791526099179,"traceId":"87321b73e1bed0b6"},{"name":"telemetry-flush","duration":62,"timestamp":126756523,"id":11271,"parentId":1,"tags":{},"startTime":1791526115146,"traceId":"87321b73e1bed0b6"},{"name":"next-build","duration":126260310,"timestamp":496297,"id":1,"tags":{"buildMode":"default","version":"16.2.12","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1791525988886,"traceId":"87321b73e1bed0b6"}]
@@ -9,7 +9,7 @@ const MEMORY_RE = /(const phase1Model = await resolveMemoryModel\(\{[^}]*?fallba
9
9
  const MEMORY_DONE = /const phase1Model = await resolveMemoryModel\(\{[^}]*?fallbackRole: "smol"/;
10
10
  const EXPECTED = {
11
11
  name: 'cuelo',
12
- version: '0.10.2',
12
+ version: '0.10.4',
13
13
  distribution: 'cuelo',
14
14
  coreVersion: '18.8.6',
15
15
  };
@@ -3,10 +3,10 @@ name: maker
3
3
  description: Delegated end-to-end implementation agent that investigates, edits, repairs, validates its own slice, and verifies the actual surface it changed.
4
4
  model: "@implSonnet"
5
5
  thinking-level: auto
6
- tools: [read, bash, edit, write, grep, glob, skim, draft, lsp, eval, generate_image, ast_grep, ast_edit, debug, todo, web_search, checkpoint]
6
+ tools: [read, bash, edit, write, grep, glob, skim, draft, lsp, eval, generate_image, ast_grep, ast_edit, debug, todo, web_search, windows_build, checkpoint]
7
7
  ---
8
8
 
9
- <!-- omp-global-config:generated source-hash=29515cbfea01
9
+ <!-- omp-global-config:generated source-hash=d67a93056850
10
10
  이 파일은 patches/build-agents.mjs 가 만든 빌드 산출물이다. 직접 수정하지 마라.
11
11
  고칠 곳: agent/sop/maker.md · agent/sop/_writer.md · agent/sop/_common.md
12
12
  재생성: node patches/build-agents.mjs 검사: node patches/build-agents.mjs --check
@@ -266,10 +266,13 @@ is yours end to end.
266
266
 
267
267
  ## Common Rules
268
268
 
269
- - **The task brief and every follow-up instruction from Main are written in English (user decision
270
- 2026-10-09). Every user-visible progress or final prose you write stays Korean, even though the
271
- brief is English.** Preserve required structural keys/headings, model IDs, code, commands, paths,
272
- filenames, API names, and original errors verbatim. Hidden reasoning language is not governed.
269
+ - **Main and you talk to each other in English (user decisions 2026-10-09). The task brief and every
270
+ follow-up instruction from Main are English, and so is everything you write back: progress
271
+ narration, the final report, steering checkpoints, and peer `write agent://` messages.** The user
272
+ does not read child reports directly; Main relays what matters in Korean. The only exception is a
273
+ character-summon child answering the user directly, which speaks Korean in the selected voice.
274
+ Preserve required structural keys/headings, model IDs, code, commands, paths, filenames, API
275
+ names, and original errors verbatim. Hidden reasoning language is not governed.
273
276
  - The brief's allowed boundary — the projects, paths, and ownership it names — is absolute. The
274
277
  files it points you at are where the investigation starts, not the limit of what you may read
275
278
  or change. Inside that boundary you own the direct cause of the assigned problem even when it
@@ -375,11 +378,11 @@ is yours end to end.
375
378
 
376
379
  ## Conversation
377
380
 
378
- - **Every line of prose you emit is Korean, not only the final report.** The one-line narration
379
- you write while working ("I'll start by reading...", "Now checking X") is rendered straight
380
- into the user's chat window as your own inline utterance next to your account face, so it is
381
- user-facing text, not private scratch. This covers narration between tool calls, `write agent://` messages,
382
- steering checkpoints, and code comments. The verbatim carve-out above is unchanged.
381
+ - **Every line of prose you emit is English, not only the final report.** The one-line narration
382
+ you write while working ("I'll start by reading...", "Now checking X"), narration between tool
383
+ calls, `write agent://` messages, steering checkpoints, and the final report are all read by
384
+ Main, which relays them to the user in Korean. A character-summon child answering the user
385
+ directly is the exception and speaks Korean. The verbatim carve-out above is unchanged.
383
386
  - Follow the dynamically injected `<character-voice>` block that matches the account face shown to
384
387
  the user. Each alias has its own temperament, vocabulary, rhythm, and reaction style.
385
388
  - Keep conversational turns terse, technically exact, and natural. Never turn routine speech
@@ -393,7 +393,7 @@ export function renderCharacterVoice(alias: CharacterAlias): string {
393
393
  - 성격: ${voice.temperament}
394
394
  - 표현: ${voice.expression}
395
395
  - 캐릭터의 감정선과 반응 리듬은 애니메이션 캐릭터처럼 선명하게 드러낸다. 단, 과장된 역할극, 매 문장 감탄사, 고정 캐치프레이즈, 같은 추임새 반복은 피한다.
396
- - 일반 대화·진행 발화·task child 인라인 응답은 자연스러운 한국어로 말한다. 시스템 프롬프트 안의 heading·표·목록은 정책을 정리한 것이지 사용자 응답 템플릿이 아니다. 사건 보고서 같은 제목, 명사형 목록, 고정 상태 구조를 기본값으로 쓰지 않는다.
396
+ - 일반 대화·진행 발화는 자연스러운 한국어로 말한다. 단, 일반 task child(Maker)의 진행 발화·완료 보고·체크포인트·agent 간 메시지는 Main만 읽고 Main이 사용자에게 한국어로 옮기므로 영어로 쓴다. 캐릭터 호출로 만든 child가 사용자에게 직접 하는 답만 한국어다. 시스템 프롬프트 안의 heading·표·목록은 정책을 정리한 것이지 사용자 응답 템플릿이 아니다. 사건 보고서 같은 제목, 명사형 목록, 고정 상태 구조를 기본값으로 쓰지 않는다.
397
397
  - 직전 도구 출력·로그·소스·advisory가 영어이거나 이 대화의 앞선 자기 발화가 영어로 샜더라도, 사용자에게 보이는 다음 문장은 한국어로 돌아온다. 영어 문장을 이어 쓰지 않는다.
398
398
  - 이 말투는 첫 응답에서 끝나지 않는다. 도구 결과 설명·검증 실패·위험·미확인 보고·사용자 interjection 이후 답변·진행·최종 답변 전체에서 유지한다. 검증·증거·todo·child 상태 계약은 전달할 정보를 정할 뿐 사용자 답변 템플릿을 정하지 않는다.
399
399
  - 사용자 요구나 parser/convention이 요구한 heading·목록·\`FINAL\`·\`OWNER\` 형식만 예외다. 그 밖의 루틴 응답은 답부터 자연스러운 문장으로 말하고, 필요하지 않은 상태 레이블이나 형식을 덧씌우지 않는다.
@@ -12,6 +12,7 @@ import {
12
12
  type CharacterAlias,
13
13
  } from "../character-voice";
14
14
  import { resolvePreparedTaskInput } from "../lib/prepared-task";
15
+ import { selectGitExecutor, type GitExecutor } from "../../tools/git-finalizer/wsl-git";
15
16
 
16
17
  import { matchBlockedCommand } from "./matcher";
17
18
  import { matchPowerShellSyntax } from "./powershell-syntax";
@@ -123,12 +124,15 @@ function sameMakerReservation(
123
124
 
124
125
  async function gitCommonDirectory(directory: string): Promise<string | undefined> {
125
126
  try {
127
+ const git = await selectGitExecutor(directory);
126
128
  const output = await runGit(
129
+ git,
127
130
  directory,
128
131
  ["rev-parse", "--path-format=absolute", "--git-common-dir"],
129
132
  );
130
133
  const commonDirectory = output?.trim();
131
- return commonDirectory ? normalizeRepositoryPath(commonDirectory) : undefined;
134
+ // Windows git은 `D:/repo/.git`을 돌려준다. Linux git 쪽 `/mnt/d/repo/.git`과 같은 키가 되게 되돌린다.
135
+ return commonDirectory ? normalizeRepositoryPath(git.toPosix(commonDirectory)) : undefined;
132
136
  } catch {
133
137
  return undefined;
134
138
  }
@@ -194,10 +198,11 @@ async function repositoryMatcher(
194
198
  const GIT_SPAWN_TIMEOUT_MS = 5000;
195
199
  const GIT_MAX_OUTPUT_BYTES = 64 * 1024 * 1024;
196
200
 
197
- function runGit(directory: string, args: string[], input?: string): Promise<string | undefined> {
201
+ function runGit(git: GitExecutor, directory: string, args: string[], input?: string): Promise<string | undefined> {
198
202
  const { promise, resolve: resolveOutput } = Promise.withResolvers<string | undefined>();
199
203
  const child = execFile(
200
- "git",
204
+ // /mnt/<드라이브> 저장소는 Windows git이 훨씬 빠르다(9p stat). 판정은 git_finalize·앱과 같다.
205
+ git.command,
201
206
  args,
202
207
  {
203
208
  cwd: directory,
@@ -232,11 +237,13 @@ function runGit(directory: string, args: string[], input?: string): Promise<stri
232
237
  */
233
238
  async function takeTreeSnapshot(directory: string): Promise<TreeSnapshot | undefined> {
234
239
  try {
235
- const prefixOutput = await runGit(directory, ["rev-parse", "--show-prefix"]);
240
+ const git = await selectGitExecutor(directory);
241
+ const prefixOutput = await runGit(git, directory, ["rev-parse", "--show-prefix"]);
236
242
  if (prefixOutput === undefined) return undefined;
237
243
  const scope = prefixOutput.trim().replace(/\\/g, "/");
238
244
 
239
245
  const statusOutput = await runGit(
246
+ git,
240
247
  directory,
241
248
  ["status", "--porcelain=v1", "-z", "--untracked-files=all", "--", "."],
242
249
  );
@@ -265,6 +272,7 @@ async function takeTreeSnapshot(directory: string): Promise<TreeSnapshot | undef
265
272
  const hashedPaths = entries.filter((entry) => !entry.deleted).map((entry) => entry.path);
266
273
  if (hashedPaths.length > 0) {
267
274
  const hashOutput = await runGit(
275
+ git,
268
276
  directory,
269
277
  ["hash-object", "--stdin-paths", "--no-filters"],
270
278
  hashedPaths.join("\n") + "\n",
@@ -5,7 +5,8 @@ import type { ExtensionAPI, ExtensionContext, SessionManager } from "@oh-my-pi/p
5
5
  * 응답 언어 가드. Main이 영어 브리프를 쓰다 사용자에게 보이는 진행 문장까지 영어로 새는 일(2026-10-02)을 막는다.
6
6
  * 1) 감지: 사용자에게 보이는 산문에서 원문 보존 대상(코드·URL·경로·식별자)을 뺀 글자 중 한글 비율이 낮으면 드리프트.
7
7
  * 2) 교정: 첫 드리프트 뒤 다음 요청에 한국어 복귀 알림을 한 번 넣는다(모델 호출 없음).
8
- * 3) 번역: 알림 뒤에도 또 드리프트면 그 메시지만 @tiny(HIKARI)로 번역해 원문 아래에 표시한다(모델 문맥 제외).
8
+ * 3) 번역: 드리프트한 메시지는 첫 번째부터 @tiny(HIKARI)로 번역해 원문 아래에 표시한다(모델 문맥 제외).
9
+ * 이미 나간 영어 문장을 그대로 두지 않는다(2026-10-09 사용자 요청: 다음 답만 한국어로 돌아오는 것으로는 부족하다).
9
10
  */
10
11
 
11
12
  export type TranslateReply = (text: string, ctx: ExtensionContext, signal: AbortSignal) => Promise<string>;
@@ -142,7 +143,6 @@ export function createKoreanReplyGuard(translate: TranslateReply = translateWith
142
143
  { customType: REMINDER_TYPE, content: REMINDER_TEXT, display: false, attribution: "agent" },
143
144
  { deliverAs: hasToolCall ? "aside" : "nextTurn" },
144
145
  );
145
- return;
146
146
  }
147
147
  const key = hash(text);
148
148
  if (translated.has(key)) return;
@@ -486,7 +486,7 @@
486
486
  "preDispatchCrossCheck": ["requirement-omission", "unverified-assertion-as-settled-fact", "arbitrary-scope-or-acceptance-change"],
487
487
  "acceptanceChangeOwner": "only-the-user-changes-an-acceptance-condition-never-lower-it-to-make-a-slice-easier",
488
488
  "taskBriefLanguage": "english-prose-for-the-initial-dispatch-and-every-follow-up-instruction-to-a-child",
489
- "childVisibleLanguage": "korean-progress-and-final-prose",
489
+ "childVisibleLanguage": "english-progress-narration-final-reports-checkpoints-and-agent-to-agent-messages-read-by-main-main-relays-them-to-the-user-in-korean-only-character-summon-children-answering-the-user-directly-stay-korean",
490
490
  "languageVerbatimCarveOut": "structural-keys-and-headings-model-ids-code-commands-paths-filenames-api-names-and-original-errors-stay-verbatim",
491
491
  "conversationalStyle": "selected-character-voice-for-ordinary-dialogue-progress-and-inline-speech-without-default-report-headings-nominal-lists-or-fixed-status-structure",
492
492
  "bilingualFallback": "never"
@@ -769,7 +769,7 @@
769
769
  "codeAndPolicyReviewOwner": "main",
770
770
  "designJudgment": "main-or-the-owning-maker-reads-the-design-rules-and-skills-there-is-no-separate-design-role",
771
771
  "repositoryAccess": "owner-writable-inside-its-brief-boundary-only",
772
- "tools": ["read", "bash", "edit", "write", "grep", "glob", "skim", "draft", "lsp", "eval", "generate_image", "ast_grep", "ast_edit", "debug", "todo", "web_search", "checkpoint"],
772
+ "tools": ["read", "bash", "edit", "write", "grep", "glob", "skim", "draft", "lsp", "eval", "generate_image", "ast_grep", "ast_edit", "debug", "todo", "web_search", "windows_build", "checkpoint"],
773
773
  "runtimeAutoPair": "checkpoint-in-frontmatter-causes-core-createTools-to-add-rewind-to-the-restricted-session-tool-surface-rewind-is-intentionally-absent-from-frontmatter",
774
774
  "execution": "async",
775
775
  "broadValidation": "the-owner-runs-the-ordinary-project-checks-its-slice-needs-and-skips-unrelated-project-wide-suites",
@@ -13,8 +13,8 @@ globs: ["Tools/OMP_Global_Config/**"]
13
13
  - `thinking-level`은 SOP에 쓰지 않는다. 생성 기본값은 `auto`이고 모델은 `model:"@implSonnet"`이다.
14
14
  발주 때 Main이 고른 `tasks[].model`의 `:auto` selector가 child auto를 켜고, 실제 단계는 그 모델의 전체 범위에서 child가 고른다.
15
15
  모델·fallback은 `modelRoles`, 모델 분류·추론은 `routing.modelSelection`·`routing.effortSelection`이 정한다.
16
- - frontmatter 도구는 `read, bash, edit, write, grep, glob, lsp, eval,
17
- generate_image, ast_grep, ast_edit, debug, todo, web_search, checkpoint`다. core 18.3.0에는 `hub`가
16
+ - frontmatter 도구는 `read, bash, edit, write, grep, glob, skim, draft, lsp, eval,
17
+ generate_image, ast_grep, ast_edit, debug, todo, web_search, windows_build, checkpoint`다. core 18.3.0에는 `hub`가
18
18
  없고(목록에 남겨도 조용히 버려진다) `wait`는 top-level 전용이라 Maker(depth 1)에 넣어도 생기지 않으므로 둘 다 적지 않는다.
19
19
  restricted session에서 `checkpoint`를 명시하면 core `createTools`가 `rewind`를 안전 쌍으로
20
20
  자동 추가하므로 frontmatter에는 `rewind`를 중복하지 않지만 실제 runtime에는 둘 다 노출된다.
@@ -304,9 +304,11 @@ Task Guard lock·budget·소유권·`FINDING_ID`, exit status, 파일·권한·
304
304
  - 넘을 수 없는 허용 경계와 조사를 시작할 파일·심볼은 구분해 적는다. 경계 안이고 다른 Writer와
305
305
  겹치지 않으면 직접 원인이 있는 인접 파일까지 담당자가 맡는다. 사용자가 파일을 지정했으면 그
306
306
  목록이 곧 경계다. 새 프로젝트, 다른 소유자의 파일, 계약 변경, 새 위험은 Main이 먼저 조정한다.
307
- - **child에게 가는 발주 산문은 영어다(2026-10-09 사용자 결정).** 과제·배치 `context`의 첫 발주와 그 뒤
308
- `write agent://` 메시지의 조향·체크포인트 회신·재작업 인계·상태 질의를 모두 영어로 쓴다. child가 사용자
309
- 화면에 쓰는 진행·최종 응답은 브리프가 영어여도 한국어이며 선택된 character voice를 따른다.
307
+ - **Main과 child 사이의 산문은 양방향 모두 영어다(2026-10-09 사용자 결정).** 과제·배치 `context`의 첫 발주와 그 뒤
308
+ `write agent://` 메시지의 조향·체크포인트 회신·재작업 인계·상태 질의를 영어로 쓴다. child의 진행 발화·완료 보고·
309
+ 편집 전 체크포인트·peer 메시지도 Main만 읽으므로 영어다. 사용자는 child 보고를 직접 읽을 필요가 없고, Main이 필요한
310
+ 내용을 한국어로 옮겨 전한다. 예외는 캐릭터 호출(summon)로 만든 child가 사용자에게 직접 하는 답으로, 사용자에게 하는
311
+ 말이라 한국어이며 선택된 character voice를 따른다.
310
312
  `TASK_GUARD` field와 `# Target`·`# Change`·`# Acceptance` 같은 구조 heading, 모델 ID, 코드·명령·경로·
311
313
  파일명·API명·원본 오류 메시지는 원문을 유지하고, 같은 브리프를 두 언어로 병기하는 bilingual fallback은 두지 않는다.
312
314
  정본은 `harness-policy.json` `briefContextRelay.taskBriefLanguage`·`childVisibleLanguage`·
@@ -1,9 +1,12 @@
1
1
  ## Common Rules
2
2
 
3
- - **The task brief and every follow-up instruction from Main are written in English (user decision
4
- 2026-10-09). Every user-visible progress or final prose you write stays Korean, even though the
5
- brief is English.** Preserve required structural keys/headings, model IDs, code, commands, paths,
6
- filenames, API names, and original errors verbatim. Hidden reasoning language is not governed.
3
+ - **Main and you talk to each other in English (user decisions 2026-10-09). The task brief and every
4
+ follow-up instruction from Main are English, and so is everything you write back: progress
5
+ narration, the final report, steering checkpoints, and peer `write agent://` messages.** The user
6
+ does not read child reports directly; Main relays what matters in Korean. The only exception is a
7
+ character-summon child answering the user directly, which speaks Korean in the selected voice.
8
+ Preserve required structural keys/headings, model IDs, code, commands, paths, filenames, API
9
+ names, and original errors verbatim. Hidden reasoning language is not governed.
7
10
  - The brief's allowed boundary — the projects, paths, and ownership it names — is absolute. The
8
11
  files it points you at are where the investigation starts, not the limit of what you may read
9
12
  or change. Inside that boundary you own the direct cause of the assigned problem even when it
@@ -109,11 +112,11 @@
109
112
 
110
113
  ## Conversation
111
114
 
112
- - **Every line of prose you emit is Korean, not only the final report.** The one-line narration
113
- you write while working ("I'll start by reading...", "Now checking X") is rendered straight
114
- into the user's chat window as your own inline utterance next to your account face, so it is
115
- user-facing text, not private scratch. This covers narration between tool calls, `write agent://` messages,
116
- steering checkpoints, and code comments. The verbatim carve-out above is unchanged.
115
+ - **Every line of prose you emit is English, not only the final report.** The one-line narration
116
+ you write while working ("I'll start by reading...", "Now checking X"), narration between tool
117
+ calls, `write agent://` messages, steering checkpoints, and the final report are all read by
118
+ Main, which relays them to the user in Korean. A character-summon child answering the user
119
+ directly is the exception and speaks Korean. The verbatim carve-out above is unchanged.
117
120
  - Follow the dynamically injected `<character-voice>` block that matches the account face shown to
118
121
  the user. Each alias has its own temperament, vocabulary, rhythm, and reaction style.
119
122
  - Keep conversational turns terse, technically exact, and natural. Never turn routine speech
@@ -2,7 +2,7 @@
2
2
  name: maker
3
3
  description: Delegated end-to-end implementation agent that investigates, edits, repairs, validates its own slice, and verifies the actual surface it changed.
4
4
  model: "@implSonnet"
5
- tools: [read, bash, edit, write, grep, glob, skim, draft, lsp, eval, generate_image, ast_grep, ast_edit, debug, todo, web_search, checkpoint]
5
+ tools: [read, bash, edit, write, grep, glob, skim, draft, lsp, eval, generate_image, ast_grep, ast_edit, debug, todo, web_search, windows_build, checkpoint]
6
6
  ---
7
7
 
8
8
  You are the delegated implementation Maker on the default `modelRoles.implSonnet` slot. A delegated slice
@@ -4,6 +4,9 @@ import { access, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises
4
4
  import { tmpdir } from "node:os";
5
5
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
+ import { hasPosixGitdirWorktree, usesWindowsDriveRepo } from "./wsl-git";
8
+
9
+ export { hasPosixGitdirWorktree };
7
10
 
8
11
  interface FinalizerResult {
9
12
  ok: boolean;
@@ -167,9 +170,10 @@ function parseResult(stdout: string): FinalizerResult | undefined {
167
170
  * 2026-10-08 V: 아카이브(추적 파일 21만여 개)에서 Linux `git status`는 300초를 넘겼고 finalizer 제한 시간(180초)에
168
171
  * 걸려 `exit -1`로 끊겼다. 같은 저장소의 Windows `git.exe status`는 2.5초다. 그런 저장소는 Windows PowerShell과
169
172
  * Windows git으로 같은 finalizer를 돌리고, 넘기는 경로는 `wslpath -w`로 바꾼다. 해당하지 않으면 undefined다.
173
+ * 판정(`usesWindowsDriveRepo`: `/mnt/<드라이브>/` + POSIX gitdir worktree 제외)은 command-guard·앱의 git 실행기와 같다.
170
174
  */
171
- async function windowsDrivePaths(cwd: string, paths: string[], exec: Exec): Promise<string[] | undefined> {
172
- if (process.platform !== "linux" || !process.env.WSL_DISTRO_NAME || !/^\/mnt\/[a-z]\//i.test(cwd)) return undefined;
175
+ async function windowsDrivePaths(cwd: string, files: string[], paths: string[], exec: Exec): Promise<string[] | undefined> {
176
+ if (!(await usesWindowsDriveRepo(cwd, files))) return undefined;
173
177
  const converted: string[] = [];
174
178
  for (const path of paths) {
175
179
  const result = await exec("wslpath", ["-w", path], { cwd });
@@ -179,29 +183,6 @@ async function windowsDrivePaths(cwd: string, paths: string[], exec: Exec): Prom
179
183
  return converted;
180
184
  }
181
185
 
182
- /**
183
- * WSL Linux git이 `/mnt/...` 저장소에 만든 linked worktree는 `.git` 파일에 `gitdir: /mnt/e/...` 같은 Linux 경로를 적는다.
184
- * Windows git은 이 경로를 따라가지 못해 `fatal: not a git repository`로 끝난다(2026-10-09 `.worktrees/dart-cron-order`).
185
- * 대상 파일에서 위로 올라가 처음 만나는 `.git`이 그런 파일이면 true다. 그때는 느려도 Linux git으로 마감한다.
186
- */
187
- export async function hasPosixGitdirWorktree(cwd: string, files: string[]): Promise<boolean> {
188
- for (const file of files) {
189
- let dir = dirname(resolve(cwd, file));
190
- for (;;) {
191
- const dotGit = join(dir, ".git");
192
- const info = await stat(dotGit).catch(() => undefined);
193
- if (info) {
194
- if (info.isFile() && /^gitdir:\s*\//m.test(await readFile(dotGit, "utf8"))) return true;
195
- break;
196
- }
197
- const parent = dirname(dir);
198
- if (parent === dir) break;
199
- dir = parent;
200
- }
201
- }
202
- return false;
203
- }
204
-
205
186
  const factory: CustomToolFactory = (pi) => ({
206
187
  name: "git_finalize",
207
188
  label: "Git Finalize",
@@ -232,9 +213,7 @@ const factory: CustomToolFactory = (pi) => ({
232
213
  const tempDir = await mkdtemp(join(tmpdir(), "omp-git-finalize-"));
233
214
  const requestPath = join(tempDir, "request.json");
234
215
  try {
235
- const windows = await hasPosixGitdirWorktree(pi.cwd, files)
236
- ? undefined
237
- : await windowsDrivePaths(pi.cwd, [pi.cwd, scriptPath, requestPath], exec);
216
+ const windows = await windowsDrivePaths(pi.cwd, files, [pi.cwd, scriptPath, requestPath], exec);
238
217
  await writeFile(requestPath, JSON.stringify({ cwd: windows?.[0] ?? pi.cwd, files, message }), "utf8");
239
218
  const result = await pi.exec(
240
219
  process.platform === "win32" || windows ? "powershell.exe" : "pwsh",
@@ -0,0 +1,96 @@
1
+ /**
2
+ * WSL에서 Windows 드라이브(`/mnt/<드라이브>/`) 위 저장소를 어느 git으로 다룰지 정하는 공용 규칙.
3
+ *
4
+ * Linux git은 9p로 파일마다 stat해 3,905개 파일 저장소의 `git status`가 4초 가까이 걸린다(2026-10-09 실측).
5
+ * 같은 저장소를 Windows `git.exe`(interop)에 맡기면 수십 ms다. 그래서 WSL + `/mnt/<드라이브>/` 경로면 Windows git을
6
+ * 쓴다. 단 하나는 예외다: WSL Linux git이 만든 linked worktree는 `.git` 파일에 `gitdir: /mnt/e/...` 같은 Linux 경로를
7
+ * 적어 Windows git이 따라가지 못한다. 그런 worktree는 느려도 Linux git이다.
8
+ *
9
+ * 이 파일은 `lib/wsl-git.ts`(Next 앱)와 같은 규칙의 짝이다. 하네스는 ~/.omp/agent로 복사돼 앱 번들과 분리되므로
10
+ * 한쪽이 다른 쪽을 import할 수 없다. 규칙을 바꿀 때는 두 파일을 함께 바꾼다(`lib/wsl-git.test.mjs`가 두 구현이
11
+ * 같은 판정을 내는지 확인한다).
12
+ */
13
+ import { existsSync } from "node:fs";
14
+ import { readFile, stat } from "node:fs/promises";
15
+ import { delimiter, dirname, join, resolve } from "node:path";
16
+
17
+ export interface GitExecutor {
18
+ /** execFile에 넘길 실행 파일. */
19
+ command: "git" | "git.exe";
20
+ windows: boolean;
21
+ /**
22
+ * git이 출력한 절대 경로를 호출자가 쓰는 POSIX 경로로 바꾼다. Windows git은 `D:/repo/.git`처럼 쓰므로
23
+ * `/mnt/d/repo/.git`로 돌려놓아야 Linux 쪽 경로와 같은 기준으로 비교된다. Linux git이면 그대로다.
24
+ */
25
+ toPosix(value: string): string;
26
+ }
27
+
28
+ /** WSL이고 `/mnt/<드라이브>` 아래(드라이브 루트 포함) 경로인가. */
29
+ export function isWindowsDrivePath(cwd: string): boolean {
30
+ return process.platform === "linux" && Boolean(process.env.WSL_DISTRO_NAME) && /^\/mnt\/[a-z](?:\/|$)/i.test(cwd);
31
+ }
32
+
33
+ /** `D:/a/b`·`D:\a\b`를 `/mnt/d/a/b`로 바꾼다. 드라이브 문자로 시작하지 않으면 그대로 돌려준다. */
34
+ export function windowsPathToPosix(value: string): string {
35
+ const match = /^([A-Za-z]):[\\/]*(.*)$/.exec(value);
36
+ if (!match) return value;
37
+ const rest = match[2].replace(/\\/g, "/").replace(/\/+$/, "");
38
+ return rest ? `/mnt/${match[1].toLowerCase()}/${rest}` : `/mnt/${match[1].toLowerCase()}`;
39
+ }
40
+
41
+ /**
42
+ * 시작 디렉터리에서 위로 올라가 처음 만나는 `.git`이 `gitdir: /...`(POSIX 경로)를 가리키는 파일이면 true다.
43
+ * 2026-10-09 `.worktrees/dart-cron-order`에서 Windows git이 `fatal: not a git repository`로 끝났다.
44
+ */
45
+ export async function hasPosixGitdirWorktreeFrom(startDirectory: string): Promise<boolean> {
46
+ let dir = resolve(startDirectory);
47
+ for (;;) {
48
+ const dotGit = join(dir, ".git");
49
+ const info = await stat(dotGit).catch(() => undefined);
50
+ if (info) return info.isFile() && /^gitdir:\s*\//m.test(await readFile(dotGit, "utf8"));
51
+ const parent = dirname(dir);
52
+ if (parent === dir) return false;
53
+ dir = parent;
54
+ }
55
+ }
56
+
57
+ /** 대상 파일마다 위로 올라가 POSIX gitdir worktree에 속한 파일이 하나라도 있으면 true다. */
58
+ export async function hasPosixGitdirWorktree(cwd: string, files: string[]): Promise<boolean> {
59
+ for (const file of files) {
60
+ if (await hasPosixGitdirWorktreeFrom(dirname(resolve(cwd, file)))) return true;
61
+ }
62
+ return false;
63
+ }
64
+
65
+ /**
66
+ * WSL + `/mnt/<드라이브>/` + POSIX gitdir worktree가 아님 → Windows 쪽에서 다룬다. finalizer와 git 실행기가 같은 판정을 쓴다.
67
+ * `files`가 없으면 `cwd` 디렉터리 자체에서 위로 올라가며 보고, 있으면 파일마다 부모 디렉터리에서 올라간다.
68
+ */
69
+ export async function usesWindowsDriveRepo(cwd: string, files?: string[]): Promise<boolean> {
70
+ if (!isWindowsDrivePath(cwd)) return false;
71
+ return !(await (files ? hasPosixGitdirWorktree(cwd, files) : hasPosixGitdirWorktreeFrom(cwd)));
72
+ }
73
+
74
+ const LINUX_GIT: GitExecutor = { command: "git", windows: false, toPosix: (value) => value };
75
+ const WINDOWS_GIT: GitExecutor = { command: "git.exe", windows: true, toPosix: windowsPathToPosix };
76
+
77
+ let windowsGitProbe: { path: string; found: boolean } | undefined;
78
+
79
+ /** interop이 꺼져 PATH에 git.exe가 없으면 Linux git으로 남긴다. PATH가 바뀌면 다시 본다. */
80
+ function hasWindowsGit(): boolean {
81
+ const path = process.env.PATH ?? "";
82
+ if (windowsGitProbe?.path !== path) {
83
+ windowsGitProbe = { path, found: path.split(delimiter).some((dir) => dir && existsSync(join(dir, "git.exe"))) };
84
+ }
85
+ return windowsGitProbe.found;
86
+ }
87
+
88
+ /**
89
+ * `directory`(git을 실행할 cwd)에서 쓸 git을 고른다. Windows git은 호출자의 cwd 옵션으로 실행한다.
90
+ * WSL interop이 `/mnt/<드라이브>` cwd를 Windows 경로로 넘기므로 경로 인자를 따로 바꿀 필요가 없다.
91
+ */
92
+ export async function selectGitExecutor(directory: string): Promise<GitExecutor> {
93
+ if (!hasWindowsGit()) return LINUX_GIT;
94
+ // `.git` 파일을 읽지 못하는 등 판정을 못 하면 어느 git이든 열 수 있는 Linux git으로 남긴다.
95
+ return (await usesWindowsDriveRepo(directory).catch(() => false)) ? WINDOWS_GIT : LINUX_GIT;
96
+ }