@angar2/taskery 1.0.1 → 1.0.2

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/README.md CHANGED
@@ -275,7 +275,7 @@ Node 프로젝트는 `package-lock.json`이 있으면 `npm ci`로 설치합니
275
275
 
276
276
  ## 오케스트레이션 — 한 줄 지시로 여러 태스크를 나눠 맡기기
277
277
 
278
- **오케스트레이션** = 한 세션(관리 세션)이 백로그를 태스크로 나누고, 태스크마다 새 AI 탭을 띄워 맡기고, 보고를 받아 정리하는 방식입니다. Orca 안에서 씁니다.
278
+ **오케스트레이션** = 한 세션(관리 세션)이 백로그를 태스크로 나누고, 태스크마다 새 AI 탭을 띄워 맡기고, 보고를 받아 정리하는 방식입니다. Orca 안에서 씁니다. Orca를 쓸 수 없을 때는 사용자가 직접 세션을 열어 첫 지시를 붙여 넣는 방식으로도 진행할 수 있습니다.
279
279
 
280
280
  ```
281
281
  👤 사용자
@@ -294,9 +294,9 @@ Node 프로젝트는 `package-lock.json`이 있으면 `npm ci`로 설치합니
294
294
  |---|---|
295
295
  | ① | 사용자가 관리 세션에 한 줄로 지시합니다. 예: "백로그 BL-1~3을 끝까지 진행해" |
296
296
  | ② | 관리 세션이 백로그와 `PLAN.md`를 읽고 태스크로 나눕니다. 어떤 태스크를 어떤 순서와 AI로 진행할지 표로 보여 주고 확인받습니다. 지시에 이미 정해져 있으면 다시 묻지 않습니다 |
297
- | ③ | 태스크마다 작업 폴더를 만들고(`prepare-task`), Orca 새 탭에 Claude나 Codex를 띄워 첫 지시를 보냅니다(`orca-dispatch-task`) |
297
+ | ③ | 태스크마다 작업 폴더를 만들고(`prepare-task`), Orca 새 탭에 Claude나 Codex를 띄워 첫 지시를 보냅니다(`orca-dispatch-task`). 첫 지시는 늘 같은 틀(기본 정보·작업 방식·이번 태스크 지시)로 나갑니다. Orca를 쓸 수 없거나 사용자가 원하면, 관리 세션이 첫 지시를 보여 주고 사용자가 직접 연 세션에 붙여 넣습니다 |
298
298
  | ④ | 각 탭이 자기 작업 폴더에서 기획부터 마무리까지 진행합니다. 끝났을 때, 확인을 기다릴 때, 질문이 있을 때, 막혔을 때 보고함에 한 줄 남깁니다(`report-task`) |
299
- | ⑤ | 관리 세션은 기다리는 동안 잠들어 있어 토큰을 쓰지 않습니다(`wait-reports`). 보고가 오거나 10분이 지나면 깨어나 처리합니다 |
299
+ | ⑤ | 관리 세션은 기다리는 동안 잠들어 있어 토큰을 쓰지 않습니다(`wait-reports`). 보고가 오거나 60분이 지나면 깨어나 처리합니다 |
300
300
  | ⑥ | 사용자 확인이 필요하면 관리 세션이 "TASK-108 탭에서 화면 확인해 주세요"라고 알립니다. 사용자는 그 탭에서 직접 답합니다 |
301
301
  | ⑦ | 끝난 태스크는 병합 잠금 안에서 하나씩 dev에 병합됩니다(`merge-task`) |
302
302
  | ⑧ | 관리 세션이 끝난 태스크의 작업 폴더와 탭을 치우고(`close-task`), 시작할 수 있는 다음 태스크로 빈자리를 채웁니다. 그리고 ③으로 돌아갑니다 |
@@ -345,7 +345,7 @@ Node 프로젝트는 `package-lock.json`이 있으면 `npm ci`로 설치합니
345
345
 
346
346
  ## 빠른 시작
347
347
 
348
- > **준비물**: Node.js 18 이상, git 2.31 이상, 위 두 에이전트 중 하나 이상. 오케스트레이션을 쓰려면 Orca가 필요합니다.
348
+ > **준비물**: Node.js 18 이상, git 2.31 이상, 위 두 에이전트 중 하나 이상. 오케스트레이션은 Orca를 기본으로 씁니다(Orca가 없으면 태스크 세션을 직접 엽니다).
349
349
 
350
350
  ### 1. 설치
351
351
 
@@ -490,7 +490,7 @@ taskery 파일은 그 리포에만 적용되는 git 무시 목록(`.git/info/exc
490
490
  - PR로 병합하는 방식은 다루지 않습니다. 병합은 로컬에서만 합니다.
491
491
  - 태스크 문서와 백로그는 git 이력에 남지 않습니다.
492
492
  - 빌드 결과 복제는 macOS에서만 됩니다. 다른 환경에서는 평소처럼 빌드합니다.
493
- - 오케스트레이션은 Orca 안에서만 됩니다.
493
+ - 오케스트레이션의 탭 자동 띄우기는 Orca 안에서만 됩니다. 그 밖에서는 사용자가 태스크 세션을 직접 열고 첫 지시를 붙여 넣습니다.
494
494
  - Codex는 작업 폴더로 옮겨 갈 수 없습니다. Codex로 태스크를 하려면 오케스트레이션이 Codex 탭을 띄우거나, 사용자가 그 작업 폴더에서 Codex를 엽니다.
495
495
 
496
496
  ---
package/bin/commands.js CHANGED
@@ -113,12 +113,16 @@ const COMMANDS = [
113
113
  },
114
114
  {
115
115
  name: 'orca-dispatch-task',
116
- summary: 'Orca 새 탭에 태스크 세션을 띄우고 첫 지시문을 보낸다(Orca 전용). 탭 handle은 .state에 기록한다',
116
+ summary: 'Orca 새 탭에 태스크 세션을 띄우고 첫 지시문을 보낸다(탭 handle은 .state에 기록). --manual이면 띄우지 않고 사용자가 붙여 넣을 첫 지시문을 출력한다',
117
117
  args: [
118
118
  TASK,
119
- { name: 'agent', desc: 'claude · codex (필수)' },
120
- { name: 'model', desc: '에이전트 모델 — 사용자가 정한 것 (필수)' },
121
- { name: 'note', desc: '첫 지시문 끝에 붙일 오케스트레이션의 말 한 줄' },
119
+ { name: 'agent', desc: 'claude · codex (--manual이 아니면 필수)' },
120
+ { name: 'model', desc: '에이전트 모델 — 사용자가 정한 것 (--manual이 아니면 필수)' },
121
+ { name: 'manual', type: 'bool', desc: '손 전달 — 탭을 띄우지 않고 작업 폴더·실행 안내·첫 지시문을 출력한다. Orca를 쓸 수 없거나 사용자가 태스크 세션을 직접 열겠다고 했을 때' },
122
+ { name: 'scope', desc: "첫 지시문 '맡는 일' 칸 — 이번 태스크가 맡는 일" },
123
+ { name: 'avoid', desc: "첫 지시문 '손대지 않는 것' 칸" },
124
+ { name: 'read', desc: "첫 지시문 '먼저 읽을 것' 칸 — 태스크 문서·플랜 문서 말고 더 읽을 문서" },
125
+ { name: 'extra', desc: "첫 지시문 '그 밖' 칸 — 위 칸에 들지 않는 이번 태스크만의 지시" },
122
126
  ],
123
127
  run: orcaDispatchTask,
124
128
  },
@@ -130,7 +134,7 @@ const COMMANDS = [
130
134
  },
131
135
  {
132
136
  name: 'wait-reports',
133
- summary: '안 읽은 보고가 생기면 그 줄들을 출력하고 끝난다. 보고 없이 10분이면 오래 조용한 태스크 탭 목록과 함께 끝난다(백그라운드 셸로 건다)',
137
+ summary: '안 읽은 보고가 생기면 출력하고 끝난다. 보고 없이 60분이면 오래 조용한 태스크 탭 목록과 함께 끝난다(백그라운드 셸로 건다)',
134
138
  args: [],
135
139
  cliOnly: true,
136
140
  run: waitReports,
package/bin/orche.js CHANGED
@@ -6,7 +6,7 @@ const L = require('./lib');
6
6
 
7
7
  const AGENTS = ['claude', 'codex'];
8
8
  // 보고 없이 기다리는 시간이자 '오래 조용한 탭'의 기준. 환경 변수는 시험 전용 통로다
9
- const WAIT_MS = Number(process.env.TASKERY_WAIT_REPORTS_MS) || 10 * 60 * 1000;
9
+ const WAIT_MS = Number(process.env.TASKERY_WAIT_REPORTS_MS) || 60 * 60 * 1000;
10
10
  const POLL_MS = Math.min(1000, WAIT_MS);
11
11
  // 첫 지시문을 보낸 뒤 턴 시작을 지켜보는 시간(orca terminal send --wait-submit)
12
12
  const SUBMIT_WAIT_SEC = 30;
@@ -45,10 +45,10 @@ function shq(v) {
45
45
  return /^[A-Za-z0-9_.\/:=@%+-]+$/.test(v) ? v : `'${String(v).replace(/'/g, `'\\''`)}'`;
46
46
  }
47
47
 
48
- // 에이전트 실행 명령 — 권한·승인 옵션은 붙이지 않는다(사용자 설정 그대로, I5)
48
+ // 에이전트 실행 명령 — 권한·승인 옵션은 붙이지 않는다(사용자 설정 그대로, I5). 모델이 없으면 모델 옵션을 뺀다
49
49
  function agentCommand(main, agent, model) {
50
- if (agent === 'claude') return `claude --model ${shq(model)}`;
51
- return `codex -m ${shq(model)} --add-dir ${shq(path.join(main, '.taskery'))}`;
50
+ if (agent === 'claude') return model ? `claude --model ${shq(model)}` : 'claude';
51
+ return `codex ${model ? `-m ${shq(model)} ` : ''}--add-dir ${shq(path.join(main, '.taskery'))}`;
52
52
  }
53
53
 
54
54
  // 단계 표에서 비어 있는 첫 단계의 스킬 — 새 태스크면 task-plan
@@ -63,19 +63,40 @@ function oneLine(text) {
63
63
  return String(text).replace(/\s*\n\s*/g, ' ').trim();
64
64
  }
65
65
 
66
- // 첫 지시문 틀(§5-4) — 마지막 줄은 --note를 줬을 때만
67
- function firstInstruction(st, note) {
66
+ // 첫 지시문 '■ 이번 태스크 지시' 칸 — 인자 이름과 칸 이름, 나오는 순서
67
+ const BRIEF = [
68
+ ['scope', '맡는 일'],
69
+ ['avoid', '손대지 않는 것'],
70
+ ['read', '먼저 읽을 것'],
71
+ ['extra', '그 밖'],
72
+ ];
73
+
74
+ // 첫 지시문 틀(§5-4) — '■ 이번 태스크 지시' 칸은 넘긴 칸이 있을 때만
75
+ function firstInstruction(st, brief = {}) {
68
76
  const label = L.taskLabel(st.num);
69
- const lines = [
77
+ const blocks = [
70
78
  `[오케스트레이션] taskery 태스크 ${label} 「${st.title}」를 맡는다. 이 세션은 태스크 세션이다.`,
71
- `- 태스크 문서: ${st.doc} — 먼저 읽는다.`,
72
- `- 시작: ${startSkill(st)}부터 한다. 태스크 만들기(task-init)와 워크트리 준비는 끝났다.`,
73
- `- 진행 범위: ${st.range}. 범위와 무관하게 사용자 확인·FAIL·풀지 못한 충돌에서는 멈춘다.`,
74
- `- 보고: 마무리 완료(병합, 병합이 없는 태스크는 merge-task가 건너뛴 뒤)·사용자 확인 대기·질문·막힘 때 report-task ${label} "<한 줄>"을 부른다.`,
75
- '- 마무리(merge-task)까지 끝나면 보고하고 멈춘다. 워크트리 정리와 태스크 닫기는 오케스트레이션이 한다.',
79
+ [
80
+ '■ 기본 정보',
81
+ `- 태스크 문서: ${st.doc}`,
82
+ `- 플랜 문서: .taskery/plans/${st.plan}/PLAN.md`,
83
+ `- 시작 단계: ${startSkill(st)} (태스크 만들기(task-init)와 워크트리 준비는 끝났다)`,
84
+ `- 진행 범위: ${st.range}`,
85
+ ],
86
+ [
87
+ '■ 작업 방식',
88
+ '- 태스크 문서와 플랜 문서를 먼저 읽는다.',
89
+ '- 맡은 일 밖은 손대지 않는다. 필요해 보이면 report-task로 묻는다.',
90
+ '- 진행 범위와 무관하게 사용자 확인·FAIL·풀지 못한 충돌에서는 멈춘다.',
91
+ '- 사용자 확인은 그대로 따라 할 수 있는 실행 절차(명령·누를 곳·기대 결과)와 함께 요청한다.',
92
+ '- 규칙 파일(.taskery/rules/·AGENTS.md)은 사용자 승인 없이 고치지 않는다.',
93
+ `- 보고: 마무리 완료(병합, 병합이 없는 태스크는 merge-task가 건너뛴 뒤)·사용자 확인 대기·질문·막힘 때 report-task ${label} "<한 줄>"을 부른다.`,
94
+ '- 멈춤: 마무리(merge-task)까지 끝나면 보고하고 멈춘다. 워크트리 정리와 태스크 닫기는 오케스트레이션이 한다.',
95
+ ],
76
96
  ];
77
- if (note && oneLine(note)) lines.push(`- 오케스트레이션이 전하는 말: ${oneLine(note)}`);
78
- return lines.join('\n');
97
+ const given = BRIEF.map(([key, name]) => [name, brief[key] == null ? '' : oneLine(brief[key])]).filter(([, v]) => v);
98
+ if (given.length) blocks.push(['■ 이번 태스크 지시', ...given.map(([name, v]) => `- ${name}: ${v}`)]);
99
+ return blocks.map((b) => (Array.isArray(b) ? b.join('\n') : b)).join('\n\n');
79
100
  }
80
101
 
81
102
  // 지시문을 보내고 턴 시작이 관측됐는지 돌려준다. 받지 않았으면 refused. 관측할 수 없는 곳(영수증 없음·provider가 보고 못 함)은 보낸 것으로 본다
@@ -98,27 +119,50 @@ function onScreen(handle, marker) {
98
119
  }
99
120
  }
100
121
 
122
+ // 손 전달(--manual) — 탭을 띄우지 않고 사용자에게 전할 작업 폴더·실행 안내·첫 지시문을 돌려준다
123
+ function manualDispatch(main, st, a, brief) {
124
+ const prevTab = st.tab;
125
+ // 남은 탭 기록을 지운다 — 남겨 두면 wait-reports가 그 handle을 계속 확인한다
126
+ if (prevTab) {
127
+ delete st.tab;
128
+ L.writeState(main, st);
129
+ L.syncDoc(main, st);
130
+ }
131
+ const run = a.agent ? agentCommand(main, a.agent, a.model && String(a.model)) : `Claude Code는 claude, Codex는 ${agentCommand(main, 'codex')}`;
132
+ return [
133
+ '손 전달 — 탭을 띄우지 않았다. 사용자에게 아래를 전하고, 사용자가 작업 폴더에서 에이전트 세션을 열어 첫 지시문을 그대로 붙여 넣게 한다.',
134
+ `- 작업 폴더: ${L.workDir(main, st)}`,
135
+ `- 실행: ${run}`,
136
+ ...(prevTab ? [`- 앞서 연 탭 ${prevTab}은 기록에서 지웠다. 그 탭에서 세션이 이미 일을 시작하지 않았는지 확인하고 닫는다.`] : []),
137
+ '--- 첫 지시문 ---',
138
+ firstInstruction(st, brief),
139
+ ].join('\n');
140
+ }
141
+
101
142
  async function orcaDispatchTask(ctx, a) {
102
143
  const main = ctx.main;
144
+ L.requireInstalled(main);
145
+ if (!a.task) L.fail('orca-dispatch-task: 빠진 입력이 있다.\n- <task> — 태스크 번호 (첫 번째 인자)');
146
+ const num = L.parseTaskNum(a.task);
147
+ const st = L.readState(main, num);
148
+ const label = L.taskLabel(num);
149
+ if (st.closed) L.fail(`${label}은 이미 닫혔다.`);
150
+ const dir = L.workDir(main, st);
151
+ if (!fs.existsSync(dir)) L.fail(`orca-dispatch-task: ${label}의 워크트리가 없다(${dir}). 'status'로 확인한다.`);
152
+ if (a.agent && !AGENTS.includes(a.agent)) L.fail(`orca-dispatch-task: --agent '${a.agent}'을 모른다. ${AGENTS.join(' · ')} 중 하나를 넣는다.`);
153
+ const brief = { scope: a.scope, avoid: a.avoid, read: a.read, extra: a.extra };
154
+ if (a.manual) return manualDispatch(main, st, a, brief);
103
155
  if (!L.inOrca()) {
104
156
  L.fail(
105
- 'orca-dispatch-task: Orca 탭 안이 아니라 태스크 세션을 띄울 수 없다(Orca 전용 — Orca 환경 변수 ORCA_TERMINAL_HANDLE이 없다). ' +
106
- 'Orca 탭에서 연 오케스트레이션 세션에서 부르거나, 사용자가 그 태스크의 워크트리에서 직접 세션을 연다.',
157
+ 'orca-dispatch-task: Orca 탭 안이 아니라 태스크 세션을 띄울 수 없다(Orca 환경 변수 ORCA_TERMINAL_HANDLE이 없다). ' +
158
+ 'Orca 탭 안에서 불렀다면 MCP 도구가 아니라 CLI(npx @angar2/taskery orca-dispatch-task …)로 다시 부른다. ' +
159
+ 'Orca를 쓸 수 없으면 --manual로 다시 불러 사용자에게 손 전달한다.',
107
160
  );
108
161
  }
109
- L.requireInstalled(main);
110
162
  const missing = [];
111
- if (!a.task) missing.push('<task> — 태스크 번호 (첫 번째 인자)');
112
163
  if (!a.agent) missing.push(`--agent — ${AGENTS.join(' · ')}`);
113
164
  if (!a.model) missing.push('--model — 에이전트 모델 (사용자가 정한 것)');
114
165
  if (missing.length) L.fail(`orca-dispatch-task: 빠진 입력이 있다.\n${missing.map((m) => `- ${m}`).join('\n')}`);
115
- if (!AGENTS.includes(a.agent)) L.fail(`orca-dispatch-task: --agent '${a.agent}'을 모른다. ${AGENTS.join(' · ')} 중 하나를 넣는다.`);
116
- const num = L.parseTaskNum(a.task);
117
- const st = L.readState(main, num);
118
- const label = L.taskLabel(num);
119
- if (st.closed) L.fail(`${label}은 이미 닫혔다.`);
120
- const dir = L.workDir(main, st);
121
- if (!fs.existsSync(dir)) L.fail(`orca-dispatch-task: ${label}의 워크트리가 없다(${dir}). 'status'로 확인한다.`);
122
166
 
123
167
  const command = agentCommand(main, a.agent, String(a.model));
124
168
  const created = orca(['terminal', 'create', '--worktree', `path:${dir}`, '--title', label, '--command', command]);
@@ -131,7 +175,7 @@ async function orcaDispatchTask(ctx, a) {
131
175
  L.writeState(main, fresh);
132
176
  L.syncDoc(main, fresh);
133
177
 
134
- const text = firstInstruction(fresh, a.note);
178
+ const text = firstInstruction(fresh, brief);
135
179
  const manual = `탭 화면을 'orca terminal read --terminal ${handle}'로 확인하고, 준비되면 아래 첫 지시문을 'orca terminal send --terminal ${handle} --text "<첫 지시문>" --enter'로 보낸다.\n--- 첫 지시문 ---\n${text}`;
136
180
  let waited;
137
181
  try {
@@ -170,7 +214,7 @@ async function orcaDispatchTask(ctx, a) {
170
214
  `- 폴더: ${dir}`,
171
215
  `- 첫 지시문을 보냈다(시작: ${startSkill(fresh)}, 범위: ${fresh.range}).`,
172
216
  ...(sendNote ? [`- 알림: ${sendNote}`] : []),
173
- '다음: wait-reports를 백그라운드 셸로 걸어 둔다.',
217
+ '다음: wait-reports가 걸려 있지 않으면 백그라운드 셸로 걸어 둔다.',
174
218
  ].join('\n');
175
219
  }
176
220
 
@@ -223,18 +267,24 @@ function quietTabs(main) {
223
267
  return out;
224
268
  }
225
269
 
270
+ // 끝날 때 열린 태스크가 있으면 다시 걸라는 줄을 붙인다 — 다시 걸지 않으면 그 뒤의 보고를 놓친다
271
+ function withRearm(main, lines) {
272
+ if (L.listStates(main).some((st) => !st.closed)) lines.push('맡긴 태스크가 남아 있으면 곧바로 wait-reports를 다시 걸어 둔다(사용자 답을 기다리는 중이어도).');
273
+ return lines.join('\n');
274
+ }
275
+
226
276
  async function waitReports(ctx) {
227
277
  const main = ctx.main;
228
278
  L.requireInstalled(main);
229
279
  const until = Date.now() + WAIT_MS;
230
280
  for (;;) {
231
281
  const lines = takeUnread(main);
232
- if (lines.length) return lines.join('\n');
282
+ if (lines.length) return withRearm(main, lines);
233
283
  if (Date.now() >= until) break;
234
284
  await new Promise((r) => setTimeout(r, Math.min(POLL_MS, Math.max(0, until - Date.now()))));
235
285
  }
236
286
  const quiet = quietTabs(main);
237
- return [`보고 없이 ${L.minutes(WAIT_MS)}이 지났다.`, `오래 조용한 탭: ${quiet.length ? '' : '없음'}`.trimEnd(), ...quiet].join('\n');
287
+ return withRearm(main, [`보고 없이 ${L.minutes(WAIT_MS)}이 지났다.`, `오래 조용한 탭: ${quiet.length ? '' : '없음'}`.trimEnd(), ...quiet]);
238
288
  }
239
289
 
240
290
  module.exports = { orcaDispatchTask, reportTask, waitReports, firstInstruction };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angar2/taskery",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "AI 코딩 에이전트(Claude Code·Codex)에게 일을 태스크 단위로 맡겨 기획·개발·테스트·병합까지 진행하게 하는 작업 체계",
5
5
  "bin": {
6
6
  "taskery": "bin/taskery.js",
@@ -44,9 +44,9 @@
44
44
  | `backlog-add "<제목>" [--type <종류>]` | 백로그 번호를 발급해 `BACKLOG.md` 열린 항목 맨 위에 빈 양식 |
45
45
  | `backlog-get [BL-번호]` | 번호를 주면 그 항목 전문, 없으면 열린 항목 목록 |
46
46
  | `backlog-mark <BL-번호> <TASK>` | `--from` 없이 연 태스크를 백로그 항목에 연결 |
47
- | `orca-dispatch-task <TASK> --agent claude\|codex --model <모델> [--note "<한 줄>"]` | 오케스트레이션이 Orca 새 탭에 태스크 세션을 띄우고 첫 지시문을 보낸다(Orca 전용). 탭 handle은 `.state`에 남는다 |
47
+ | `orca-dispatch-task <TASK> [--agent claude\|codex --model <모델>] [--manual] [--scope "<맡는 일>"] [--avoid "<손대지 않는 것>"] [--read "<먼저 읽을 것>"] [--extra "<그 밖>"]` | Orca 새 탭에 태스크 세션을 띄우고 첫 지시문을 보낸다(`--manual`이 아니면 `--agent`·`--model` 필수, 탭 handle은 `.state`에 남는다). `--manual`이면 띄우지 않고 작업 폴더·실행 안내와 함께 사용자가 붙여 넣을 첫 지시문을 출력한다(손 전달) |
48
48
  | `report-task <TASK> "<한 줄>"` | 태스크 세션이 오케스트레이션에 보고 한 줄을 남긴다(`.taskery/reports.log`) |
49
- | `wait-reports` | 오케스트레이션이 백그라운드 셸로 걸어 두는 대기. 안 읽은 보고가 생기면 출력하고 끝나고, 보고 없이 10분이면 오래 조용한 태스크 탭 목록과 함께 끝난다 (CLI만) |
49
+ | `wait-reports` | 오케스트레이션이 백그라운드 셸로 걸어 두는 대기. 안 읽은 보고가 생기면 출력하고 끝나고, 보고 없이 60분이면 오래 조용한 태스크 탭 목록과 함께 끝난다 (CLI만) |
50
50
  | `init` · `update` · `add <claude\|codex>` | 설치 · 갱신 · 플랫폼 추가 (CLI만) |
51
51
 
52
52
  태스크 명령(`approve-plan`~`merge-task`)에 `--range "<새 범위>"`를 붙이면 태스크 문서 헤더 표의 범위 칸만 바뀐다.
@@ -110,3 +110,4 @@
110
110
  | 2026-10-01 | S4 — 로컬 규칙 틀(`TEST_RULE.local.md`·`DEV_RULE.local.md`)을 `init`이 만든다는 줄을 더했다 |
111
111
  | 2026-10-01 | S3 — 오케스트레이션 명령 `orca-dispatch-task`·`report-task`·`wait-reports`와 `reports.log`를 더했다 |
112
112
  | 2026-10-01 | S2 — `prune`·`backlog-*`·`prepare-task --from`·`status`의 시작할 수 있는 태스크, 새 워크트리 준비(빌드 결과 폴더 APFS 복제·exclude 등록·닫을 때 본진 씨앗 갱신·`npm ci`)를 더했다 |
113
+ | 2026-10-09 | 첫 지시문 정형화 — orca-dispatch-task의 --note를 칸별 인자로 바꾸고 손 전달(--manual)을 더했다. wait-reports 기다리는 시간을 60분으로 늘렸다 |
@@ -9,6 +9,9 @@ description: 오케스트레이션 — 백로그를 태스크로 기획하고,
9
9
 
10
10
  - 태스크는 서브에이전트에 맡기지 않는다. 태스크 세션은 Orca 새 탭의 독립 세션이며, 사용자가 그 탭에서 확인하고 개입할 수 있어야 한다.
11
11
  - 태스크마다 `task-init`(여는 일)을 이 세션이 하고, `orca-dispatch-task`로 태스크 세션을 띄운다. 에이전트와 모델은 사용자가 정하고, 정하지 않았으면 태스크 구성 확인 때 묻는다.
12
+ - 첫 지시문의 역할·작업 방식은 명령이 같은 문장으로 채운다. 이 세션은 이번 태스크만의 지시를 칸별로 넘긴다 — `--scope`(맡는 일)·`--avoid`(손대지 않는 것)·`--read`(태스크 문서·플랜 문서 말고 먼저 읽을 것)·`--extra`(그 밖). 리포 규칙(`*.local.md`)에 이미 있는 것과 첫 지시문에 이미 있는 것(보고·멈춤 등)은 다시 쓰지 않는다. 필요 없는 칸은 비운다.
13
+ - **손 전달**: Orca를 쓸 수 없거나(Orca 밖·탭 띄우기 실패) 사용자가 태스크 세션을 직접 열겠다고 하면 `orca-dispatch-task --manual`로 부른다. 명령이 내는 작업 폴더·실행 안내·첫 지시문을 사용자에게 전해 붙여 넣어 달라고 하고, `wait-reports`가 걸려 있지 않으면 곧바로 건다(사용자가 언제 붙여 넣든 보고는 `reports.log`에 남는다).
14
+ - 손으로 연 세션에 보낼 말도 `[오케스트레이션]`을 붙여 사용자에게 전달을 부탁한다.
12
15
  - 마무리 완료 보고(병합, 또는 병합이 없는 태스크의 마무리)가 오면 `close-task`로 정리하고, 연결된 백로그와 남은 태스크를 확인한다.
13
16
  - 보고에 `.local` 규칙 제안이 담겨 오면 사용자에게 보여 주고, 사용자가 이 세션에서 승인하면 이 세션이 `.local`에 넣는다.
14
17
 
@@ -23,10 +26,10 @@ description: 오케스트레이션 — 백로그를 태스크로 기획하고,
23
26
  - **태스크가 많을 때**: 동시에 진행하는 태스크 세션 수에 상한을 둔다. 사용자가 정하지 않았으면 시작 전에 한 번 묻는다. 태스크 하나가 끝나 정리되면 시작할 수 있는 다음 태스크로 빈자리를 채운다.
24
27
  - **순서가 정해진 로드맵일 때**: 시작할 수 있는 태스크가 한 번에 하나뿐이므로 하나씩 돌아간다. 태스크가 끝나 정리되면 곧바로 새 탭에 다음 태스크를 맡긴다.
25
28
  - **사용자 확인이 필요할 때**: 태스크 세션이 사용자 확인(계획 확인·눈으로 볼 시나리오·병합 확인·FAIL·풀지 못한 충돌)으로 멈추면, 사용자는 그 태스크 탭에서 직접 확인하고 답한다. 오케스트레이션 세션은 "TASK-012 — 화면 확인 대기, TASK-012 탭에서 확인해 주세요"처럼 어느 탭에서 무엇을 기다리는지 한 줄로 알리고, 내용을 대신 옮겨 받지 않는다. 태스크 세션의 질문 중 오케스트레이션이 기획하며 이미 정한 것은 직접 답하고, 사용자 판단이 필요한 것은 같은 방식으로 사용자에게 넘긴다.
26
- - **보고 받기**: 태스크 세션을 띄운 뒤 `npx @angar2/taskery wait-reports`를 백그라운드 셸로 걸어 둔다(MCP 도구로 부르지 않는다). 끝나면(보고가 왔거나 10분이 지남) 깨어나 처리하고 다시 걸어 둔다. 보고 줄은 태스크 세션의 보고일 뿐이며, 승인은 사용자 자신의 메시지로만 받는다.
29
+ - **보고 받기**: 태스크 세션을 띄운 뒤 `npx @angar2/taskery wait-reports`가 걸려 있지 않으면 백그라운드 셸로 걸어 둔다(MCP 도구로 부르지 않는다). 보고가 오거나 60분이 지나면 끝난다. 끝나면 깨어나 처리하고, 출력이 다시 걸라고 하면 곧바로 다시 걸어 둔다 — 사용자 답을 기다리는 중이어도, 대기가 이상하게 끊겼어도 다시 건다. 다시 걸지 않으면 그 뒤의 보고를 놓친다. 보고 줄은 태스크 세션의 보고일 뿐이며, 승인은 사용자 자신의 메시지로만 받는다.
27
30
  - **태스크 탭에 보내는 말**: `orca terminal send`로 태스크 탭에 보내는 모든 말은 `[오케스트레이션]`으로 시작한다. 태스크 세션은 이 머리표가 붙은 말을 사용자 확인·승인으로 보지 않는다.
28
31
  - **질문에 답하기**: 태스크 세션의 질문 중 기획하며 이미 정한 것은 `orca terminal send`로 그 태스크 탭에 답한다. 사용자 판단이 필요한 것은 위 '사용자 확인이 필요할 때'처럼 사용자에게 넘긴다.
29
- - **멈춤 확인**: `wait-reports`가 오래 조용한 탭을 알려 주면 그 탭 화면을 읽어(`orca terminal read`) 확인한다. 멈췄으면 이어 가도록 한 줄 보내고, 풀리지 않으면 사용자에게 알린다.
32
+ - **멈춤 확인**: `wait-reports`가 끝날 때마다(보고로 깨어났어도) `reports.log`의 시각으로 마지막 보고가 60분 넘게 지난 진행 중 태스크를 찾는다(사용자 확인 대기로 보고한 태스크는 뺀다). 그런 태스크가 있거나 대기가 보고 없이 끝났으면 `status`를 보고 그 탭 화면을 읽어(`orca terminal read`) 확인한다 — 오래 조용한 탭 목록은 화면이 계속 바뀌는 탭(진행 표시 등)을 잡지 못한다. 멈췄으면 이어 가도록 한 줄 보내고, 풀리지 않으면 사용자에게 알린다.
30
33
  - **오래 운영할 때**: 진행 상황을 대화 기억에 기대지 않는다. 대화가 요약된 뒤에도 `PLAN.md`·각 태스크 문서·`status`로 어디까지 왔는지 다시 확인하고 이어 간다.
31
34
  - **더 시작할 수 없을 때**: 진행 중인 태스크가 없고 시작할 수 있는 태스크도 없으면, 남은 태스크와 막힌 이유를 사용자에게 보고한다.
32
35