@kienntpixon/dieu-phoi 0.2.0 → 0.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog — @kienntpixon/dieu-phoi
2
2
 
3
+ ## 0.2.1
4
+
5
+ - **Định dạng `task_id` được kiểm:** đúng 8 ký tự `[a-z0-9]`. Một phiên thật đã tự bịa `almanac-cat-1791568325` làm root id mà không gì chặn. `plan-table --approve` (cả `--root` lẫn `task_id` từng bước), `choose --task`, `verdict --task`, `steer --task` giờ từ chối id sai: exit ≠ 0, lỗi tiếng Việt nêu id sai và gợi ý một id mới hợp lệ (`task_id "…" không hợp lệ: cần đúng 8 ký tự a-z0-9 (vd. k3f9x2ab)`), không ghi gì. `plan-table` không `--approve` chỉ cảnh báo trên stderr.
6
+ - Lệnh mới `dieu-phoi new-id [--count n] [--json]`: in task_id mới hợp lệ, mỗi dòng một id (sinh bằng `crypto`), để điều phối viên không bao giờ tự bịa id.
7
+ - Core (`hook.cjs`) export `isValidTaskId`, `newTaskId`, `taskIdError` — một chỗ duy nhất cho cả CLI và hook.
8
+ - Hook không từ chối hay bỏ sự kiện: vẫn nhận dạng cũ `[A-Za-z0-9_-]{1,32}` để log/phiên cũ còn đọc được, nhưng `dispatch` có id sai định dạng mang thêm `task_id_invalid: true` (trường bổ sung, `schema` vẫn 1). `doctor` có dòng `task-ids`: CẢNH BÁO khi có dispatch như vậy trong 7 ngày gần đây.
9
+ - Hook `SessionStart(compact)` thêm trường `systemMessage`: một dòng tiếng Việt cho người dùng, vd. `dieu-phoi: đã nạp lại trạng thái điều phối sau khi nén (6 task, 2 agent đang chạy, 2 bước chưa giao)` — cùng số đếm với dòng tiêu đề trạng thái. Phiên không có dispatch của dieu-phoi vẫn không in gì.
10
+
3
11
  ## 0.2.0
4
12
 
5
13
  - **Nâng cấp:** **dán lại `sheet/Code.gs` và deploy bản mới TRƯỚC khi nâng CLI** — Code.gs cũ bỏ qua (làm rơi) các trường mới `session_id, orch_ctx_tokens, orch_cum_usd, orch_ctx_baseline, orch_compactions` của bản ghi được đẩy lên trong lúc chưa cập nhật; sau đó `npm i -g @kienntpixon/dieu-phoi@0.2.0` rồi `dieu-phoi setup`.
package/README.md CHANGED
@@ -43,7 +43,8 @@ Khởi động lại Claude Code sau khi cài. Kiểm tra bằng `dieu-phoi doct
43
43
  | `choose --type T --score N [--urgency U] [--reversible y\|n] [--verifiable y\|n] [--role R] [--task ID] [--override M/E \| --approved M/E] [--skill-dir P] [--seed S] [--json]` | đề xuất model + effort kèm lý do, `action`, `role`, ước tính chi phí/thời gian. `--override` cần `--task`. Kèm `orch_ctx_tokens`, `orch_ctx_growth`, `orch_ctx_over` (như `ctx`); vượt trần mềm → thêm một dòng cảnh báo |
44
44
  | `ctx [--session <id>] [--json]` | context phiên điều phối: `ctx_tokens` (request cuối), `ctx_baseline` (request đầu: system prompt + tools), `ctx_growth`, chi phí cộng dồn `cum_usd`, số lần đã nén `compactions`; `over` = `ctx_growth` > `orch_ctx_soft_cap` (profile, mặc định 60000 token tăng, chỉ được hạ). Session mặc định = `$CLAUDE_CODE_SESSION_ID`. JSON: `session_id, ctx_tokens, ctx_baseline, ctx_growth, cum_usd, compactions, soft_cap, over, advice`. Vượt trần → khuyên gõ `/compact` (trạng thái điều phối được nạp lại tự động). Đọc tăng dần qua cache `data/orch/`. Không đo được → exit 0, các số `null`, `over: false` |
45
45
  | `state [--session <id>] [--json]` | trạng thái điều phối của phiên dựng lại chỉ từ log: mỗi task — cổng (1/2/3/xong), vòng, tier, các lượt (agent, role, try, model/effort, running/done/pass/fail/timeout), verdict người dùng, steer cuối, thư mục chạy; nhóm theo kế hoạch đã duyệt (tier đã duyệt, bước "chưa giao", thư mục chạy theo root). Hook `SessionStart(compact)` tự nạp bản này vào ngữ cảnh sau `/compact` hoặc auto-compact. Bản chữ ≤ 8.000 ký tự |
46
- | `plan-table --file <steps.json> [--json] [--approve --root <root_task_id>]` | bảng phân công tiếng Việt (Bước \| Model/effort \| Rubric: điểm · kiểm chứng · đảo ngược \| Lý do \| Ước tính) + tổng chi phí + thời gian đường găng. Bước lỗi (override sai) hiện \"từ chối: …\", các bước khác vẫn hiện. Sau khi người dùng duyệt: `--approve --root R` ghi kế hoạch đã duyệt vào `data/plans/R.json` (cục bộ; `approval` = số lần duyệt, duyệt lại = +1, lần cũ vào `history`) và in thêm "Đã ghi kế hoạch đã duyệt (root R, lần duyệt n)". Từ chối (không ghi gì) khi bước thiếu `task_id`, `task_id` lặp, hoặc trùng `--root` |
46
+ | `plan-table --file <steps.json> [--json] [--approve --root <root_task_id>]` | bảng phân công tiếng Việt (Bước \| Model/effort \| Rubric: điểm · kiểm chứng · đảo ngược \| Lý do \| Ước tính) + tổng chi phí + thời gian đường găng. Bước lỗi (override sai) hiện \"từ chối: …\", các bước khác vẫn hiện. Sau khi người dùng duyệt: `--approve --root R` ghi kế hoạch đã duyệt vào `data/plans/R.json` (cục bộ; `approval` = số lần duyệt, duyệt lại = +1, lần cũ vào `history`) và in thêm "Đã ghi kế hoạch đã duyệt (root R, lần duyệt n)". Từ chối (không ghi gì) khi bước thiếu `task_id`, `task_id` sai định dạng, `task_id` lặp, hoặc trùng `--root`; `--root` phải đúng 8 ký tự `a-z0-9` |
47
+ | `new-id [--count n] [--json]` | in task_id mới hợp lệ (đúng 8 ký tự `a-z0-9`), mỗi dòng một id (mặc định 1, tối đa 1000; `--json`: mảng). Lấy mọi `task_id` / `root_task_id` từ đây — không tự bịa. `choose/verdict/steer --task` và `plan-table --approve` từ chối id sai định dạng (kèm gợi ý id mới); hook vẫn ghi id cũ nhưng gắn `task_id_invalid: true`, `doctor` cảnh báo |
47
48
  | `sync [--json]` | đẩy bản ghi chưa đồng bộ lên sink, lô ≤ 200, có thử lại. Bản ghi sink từ chối → `data/sync-rejected.jsonl`, `doctor` báo số lượng |
48
49
  | `retro [--json]` | tóm tắt cho vòng retro: số liệu từng ô, đếm `reason_code`, danh sách loại task, tóm tắt điều phối (`orchestration`) |
49
50
  | `uninstall [--purge]` | gỡ hook và đúng các quyền thư mục `runs` đã thêm khỏi settings (có sao lưu); `--purge` xoá cả `hook.cjs`. Dữ liệu giữ lại |
package/bin/dieu-phoi.cjs CHANGED
@@ -30,6 +30,8 @@ Cách dùng: dieu-phoi <lệnh> [tuỳ chọn]
30
30
  trạng thái điều phối của phiên dựng lại từ log (cũng được nạp tự động sau /compact)
31
31
  plan-table --file <steps.json> [--json] [--approve --root <root_task_id>]
32
32
  bảng phân công: Bước | Model/effort | Lý do | Ước tính + tổng + đường găng
33
+ new-id [--count n] [--json]
34
+ in task_id mới hợp lệ (8 ký tự a-z0-9), mỗi dòng một id — không tự bịa id
33
35
  sync đẩy bản ghi chưa đồng bộ lên sink
34
36
  retro [--json] tóm tắt cho vòng retro (không chứa nội dung)
35
37
  uninstall [--purge] gỡ hook khỏi settings.json (--purge xoá cả hook.cjs; dữ liệu giữ lại)
@@ -64,6 +66,7 @@ async function main(argv) {
64
66
  verdict: c.cmdVerdict,
65
67
  steer: c.cmdSteer,
66
68
  'plan-table': c.cmdPlanTable,
69
+ 'new-id': c.cmdNewId,
67
70
  stats: c.cmdStats,
68
71
  choose: c.cmdChoose,
69
72
  ctx: c.cmdCtx,
package/hook.cjs CHANGED
@@ -19,7 +19,7 @@ const path = require('path');
19
19
  const os = require('os');
20
20
  const crypto = require('crypto');
21
21
 
22
- const CORE_VERSION = '0.2.0';
22
+ const CORE_VERSION = '0.2.1';
23
23
  const SCHEMA = 1;
24
24
  const TASK_TYPES = [
25
25
  'search', 'extract', 'mechanical-edit', 'feature-impl', 'debug', 'refactor',
@@ -226,6 +226,34 @@ function parseTag(text) {
226
226
  return tag;
227
227
  }
228
228
 
229
+ // ------------------------------------------------------------------ task_id
230
+
231
+ // CONTRACT: task_id = exactly 8 chars [a-z0-9], obtained from `dieu-phoi new-id` (never invented).
232
+ // The tag parser still accepts the legacy 1–32 [A-Za-z0-9_-] form so old logs/sessions parse; such
233
+ // dispatches are flagged `task_id_invalid: true` instead of being dropped.
234
+ const TASK_ID_RE = /^[a-z0-9]{8}$/;
235
+ const TASK_ID_ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789';
236
+
237
+ function isValidTaskId(s) {
238
+ return typeof s === 'string' && TASK_ID_RE.test(s);
239
+ }
240
+
241
+ /** Fresh random task_id: 8 chars [a-z0-9], unbiased (rejection sampling on crypto bytes). */
242
+ function newTaskId() {
243
+ let out = '';
244
+ while (out.length < 8) {
245
+ for (const b of crypto.randomBytes(16)) {
246
+ if (b < 252 && out.length < 8) out += TASK_ID_ALPHABET[b % 36];
247
+ }
248
+ }
249
+ return out;
250
+ }
251
+
252
+ /** Vietnamese error for a bad id, with a freshly generated valid suggestion. */
253
+ function taskIdError(id) {
254
+ return `task_id "${id}" không hợp lệ: cần đúng 8 ký tự a-z0-9 (vd. ${newTaskId()}) — lấy id bằng \`dieu-phoi new-id\``;
255
+ }
256
+
229
257
  // ------------------------------------------------------------------ models / pricing
230
258
 
231
259
  function normalizeModel(m) {
@@ -1056,11 +1084,23 @@ function stateBody(state, kept) {
1056
1084
  * Vietnamese text of a session state, at most `cap` chars. When too long, done tasks go first
1057
1085
  * (oldest first), then the oldest open ones; the count of omitted tasks is stated.
1058
1086
  */
1059
- function formatSessionState(state, cap) {
1060
- const limit = cap || STATE_TEXT_CAP;
1087
+ /** Counts shared by the state header and the SessionStart systemMessage. */
1088
+ function stateCounts(state) {
1061
1089
  const open = state.tasks.filter((t) => t.gate !== 'done' && !t.pending);
1062
1090
  const pending = state.tasks.filter((t) => t.pending).length;
1063
1091
  const running = state.tasks.reduce((n, t) => n + t.attempts.filter((a) => a.status === 'running').length, 0);
1092
+ return { tasks: state.tasks.length, open, pending, running };
1093
+ }
1094
+
1095
+ /** One short Vietnamese line shown to the user after /compact re-injected the state. */
1096
+ function compactSystemMessage(state) {
1097
+ const c = stateCounts(state);
1098
+ return `dieu-phoi: đã nạp lại trạng thái điều phối sau khi nén (${c.tasks} task, ${c.running} agent đang chạy, ${c.pending} bước chưa giao)`;
1099
+ }
1100
+
1101
+ function formatSessionState(state, cap) {
1102
+ const limit = cap || STATE_TEXT_CAP;
1103
+ const { open, pending, running } = stateCounts(state);
1064
1104
  const header = `Phiên ${state.session_id}: ${state.tasks.length} task (${open.length} chưa xong, ${pending} bước chưa giao, ${running} agent đang chạy). `
1065
1105
  + `Thư mục chạy: ${state.runs_dir} (chỉ trỏ tới, không tự đọc).`;
1066
1106
  if (!state.tasks.length) return `${header}\nChưa có task nào được giao trong phiên này.`;
@@ -1093,7 +1133,10 @@ function handleSessionStart(p, ctx) {
1093
1133
  const state = buildSessionState(events, sid, { plansDir: ctx.paths.plans });
1094
1134
  const text = `${COMPACT_PREFIX}\n${formatSessionState(state, STATE_TEXT_CAP)}`;
1095
1135
  return {
1096
- stdout: JSON.stringify({ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: text } }),
1136
+ stdout: JSON.stringify({
1137
+ systemMessage: compactSystemMessage(state),
1138
+ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: text },
1139
+ }),
1097
1140
  };
1098
1141
  }
1099
1142
 
@@ -1133,6 +1176,7 @@ function buildDispatch(p, ctx) {
1133
1176
  agent_id: agentId,
1134
1177
  session: p.session_id ? sha8(p.session_id) : null,
1135
1178
  task_id: tag.task_id,
1179
+ ...(tag.task_id && !isValidTaskId(tag.task_id) ? { task_id_invalid: true } : {}),
1136
1180
  task_type: tag.task_type,
1137
1181
  score: tag.score,
1138
1182
  urgency: tag.urgency,
@@ -1444,10 +1488,10 @@ async function runSync(ctx, opts) {
1444
1488
  // ------------------------------------------------------------------ exports / main
1445
1489
 
1446
1490
  module.exports = {
1447
- runsDir, loadPlans, buildSessionState, formatSessionState, handleSessionStart, dispatchInSession, STATE_TEXT_CAP, COMPACT_PREFIX,
1491
+ runsDir, loadPlans, buildSessionState, formatSessionState, stateCounts, compactSystemMessage, handleSessionStart, dispatchInSession, STATE_TEXT_CAP, COMPACT_PREFIX,
1448
1492
  CORE_VERSION, SCHEMA, TASK_TYPES, URGENCIES, ROLES, MODEL_ALIASES, UNTAGGED,
1449
1493
  sha8, nowIso, readJsonFile, writeFileAtomic, defaultClaudeDir, resolveHome, pathsFor, loadConfig, makeCtx,
1450
- logError, computeUserHash, userHash, commonFields, parseTag, normalizeModel, loadPricing,
1494
+ logError, computeUserHash, userHash, commonFields, parseTag, TASK_ID_RE, isValidTaskId, newTaskId, taskIdError, normalizeModel, loadPricing,
1451
1495
  priceFor, usageBuckets, requestCost, parseJsonl, readTranscript, lastUuidOf,
1452
1496
  tagFromTranscript, summarizeTranscript, orchestratorUsage, orchCacheFile, findSessionTranscript, SESSION_ID_RE, HOOK_COLD_MAX_BYTES, appendEvent, eventLinesFrom, readEventLines, readEvents,
1453
1497
  loadSyncOffset, saveSyncOffset, recordRejected, unsyncedInfo, loadSegments, saveSegments, segmentFile,
package/lib/commands.cjs CHANGED
@@ -15,6 +15,14 @@ const USER_OUTCOMES = ['accept', 'reject'];
15
15
  const CAUSES = ['quality', 'scope'];
16
16
  const STEER_KINDS = ['minor', 'plan', 'redirect'];
17
17
  const VERIFY = ['tests', 'lint', 'review', 'judgment', 'none'];
18
+ const INVALID_ID_WINDOW_DAYS = 7;
19
+ const NEW_ID_MAX = 1000;
20
+
21
+ /** Throw the Vietnamese task_id error (with a fresh suggestion) unless `id` is a valid task_id. */
22
+ function requireTaskId(id) {
23
+ if (!core.isValidTaskId(id)) throw new Error(core.taskIdError(id));
24
+ return id;
25
+ }
18
26
 
19
27
  function skillDirOf(opts, ctx) {
20
28
  if (typeof opts['skill-dir'] === 'string') return path.resolve(opts['skill-dir']);
@@ -129,6 +137,11 @@ async function cmdDoctor(opts) {
129
137
  add(true, 'internal-subagents', skipped && skipped.count
130
138
  ? `bỏ qua ${skipped.count} SubagentStop của subagent nội bộ Claude Code (lần cuối ${skipped.last_ts})`
131
139
  : 'chưa bỏ qua SubagentStop nội bộ nào');
140
+ const since = Date.now() - INVALID_ID_WINDOW_DAYS * 86400000;
141
+ const badIds = events.filter((e) => e.kind === 'dispatch' && e.task_id_invalid === true && !(Date.parse(e.ts) < since));
142
+ add(!badIds.length, 'task-ids', badIds.length
143
+ ? `${badIds.length} dispatch trong ${INVALID_ID_WINDOW_DAYS} ngày gần đây có task_id sai định dạng (${Array.from(new Set(badIds.map((e) => e.task_id))).slice(0, 3).map((x) => `"${x}"`).join(', ')}) — task_id phải đúng 8 ký tự a-z0-9, lấy bằng \`dieu-phoi new-id\``
144
+ : `mọi dispatch trong ${INVALID_ID_WINDOW_DAYS} ngày gần đây có task_id đúng định dạng`, 'CẢNH BÁO');
132
145
  const orch = measureOrch(opts, ctx, profileOrNull(opts, ctx));
133
146
  if (orch.ctx_tokens !== null) {
134
147
  add(!orch.over, 'orch-ctx', `session ${orch.session_id}: ${orch.ctx_tokens} token, tăng ${orch.ctx_growth}/${orch.soft_cap}, ≈ ${fmtUsd(orch.cum_usd)} — ${orch.advice}`, 'CẢNH BÁO');
@@ -255,6 +268,7 @@ function verdictUser(opts, ctx, events) {
255
268
  const taskId = opts.task;
256
269
  const outcome = opts._[0];
257
270
  if (typeof taskId !== 'string' || !taskId) throw new Error('--task cần task_id');
271
+ requireTaskId(taskId);
258
272
  if (!USER_OUTCOMES.includes(outcome)) throw new Error(`verdict --task: outcome phải là ${USER_OUTCOMES.join('|')}`);
259
273
  let cause = typeof opts.cause === 'string' ? opts.cause : null;
260
274
  if (outcome === 'reject' && !CAUSES.includes(cause)) throw new Error(`reject cần --cause ${CAUSES.join('|')} (scope = yêu cầu đổi/thiếu, không phải lỗi model)`);
@@ -307,6 +321,7 @@ function cmdSteer(opts) {
307
321
  const kind = opts.kind;
308
322
  if (!ref && !opts.task) throw new Error('Cú pháp: dieu-phoi steer <agent_id> | --task <task_id> --kind minor|plan|redirect [--reason R]');
309
323
  if (!STEER_KINDS.includes(kind)) throw new Error(`--kind phải là ${STEER_KINDS.join('|')}`);
324
+ if (!ref) requireTaskId(String(opts.task));
310
325
  const ctx = ctxOf(opts);
311
326
  const events = core.readEvents(ctx);
312
327
  const steerId = crypto.randomBytes(4).toString('hex');
@@ -557,6 +572,7 @@ function cmdChoose(opts) {
557
572
  if (opts.override !== undefined && typeof opts.task !== 'string') {
558
573
  throw new Error('--override cần --task <task_id> (để học override theo từng task)');
559
574
  }
575
+ if (opts.task !== undefined) requireTaskId(typeof opts.task === 'string' ? opts.task : '');
560
576
  const input = chooseInput(opts, ctx, core.readEvents(ctx));
561
577
  const res = chooser.choose(input);
562
578
  const orch = measureOrch(opts, ctx, input.profile);
@@ -682,8 +698,6 @@ function buildPlanTable(rawSteps, base) {
682
698
  };
683
699
  }
684
700
 
685
- const ROOT_RE = /^[A-Za-z0-9_-]{1,32}$/;
686
-
687
701
  /**
688
702
  * Record the plan the user approved at gate 1: <data>/plans/<root>.json (local only — not an event, never
689
703
  * synced). Each re-approval increments `approval` (number of approvals for this root) and overwrites the
@@ -704,6 +718,9 @@ function planTaskIdProblems(plan, root) {
704
718
  const problems = [];
705
719
  const missing = plan.steps.filter((r) => !r.task_id).map((r) => r.step);
706
720
  if (missing.length) problems.push(`thiếu task_id ở bước ${missing.join(', ')}`);
721
+ for (const r of plan.steps) {
722
+ if (r.task_id && !core.isValidTaskId(r.task_id)) problems.push(`bước ${r.step}: ${core.taskIdError(r.task_id)}`);
723
+ }
707
724
  const by = new Map();
708
725
  for (const r of plan.steps) if (r.task_id) by.set(r.task_id, (by.get(r.task_id) || []).concat(r.step));
709
726
  for (const [id, steps] of by) if (steps.length > 1) problems.push(`task_id "${id}" bị dùng lại ở các bước ${steps.join(', ')}`);
@@ -738,15 +755,29 @@ function recordApprovedPlan(ctx, root, stepsFile, plan) {
738
755
  return Object.assign({ file }, rec);
739
756
  }
740
757
 
758
+ // ------------------------------------------------------------------ new-id
759
+
760
+ /** `dieu-phoi new-id [--count n]`: fresh valid task_ids, one per line (the orchestrator never invents ids). */
761
+ function cmdNewId(opts) {
762
+ const raw = opts.count === undefined ? '1' : String(opts.count);
763
+ const n = /^\d+$/.test(raw) ? Number(raw) : NaN;
764
+ if (!Number.isInteger(n) || n < 1 || n > NEW_ID_MAX) throw new Error(`--count phải là số nguyên 1–${NEW_ID_MAX}`);
765
+ const ids = new Set();
766
+ while (ids.size < n) ids.add(core.newTaskId());
767
+ const list = Array.from(ids);
768
+ return { code: 0, out: opts.json ? JSON.stringify(list) : list.join('\n') };
769
+ }
770
+
741
771
  function mdCell(s) {
742
772
  return String(s).replace(/\|/g, '\\|').replace(/\r?\n/g, ' ');
743
773
  }
744
774
 
745
775
  function cmdPlanTable(opts) {
746
776
  if (typeof opts.file !== 'string') throw new Error('Cú pháp: dieu-phoi plan-table --file <steps.json> [--json] [--approve --root <root_task_id>]');
747
- if (opts.approve && (typeof opts.root !== 'string' || !ROOT_RE.test(opts.root))) {
748
- throw new Error('--approve cần --root <root_task_id> (chữ, số, - _; tối đa 32 ký tự)');
777
+ if (opts.approve && typeof opts.root !== 'string') {
778
+ throw new Error(`--approve cần --root <root_task_id> (đúng 8 ký tự a-z0-9, vd. ${core.newTaskId()})`);
749
779
  }
780
+ if (opts.approve) requireTaskId(opts.root);
750
781
  let raw;
751
782
  try {
752
783
  raw = JSON.parse(fs.readFileSync(path.resolve(opts.file), 'utf8').replace(/^/, ''));
@@ -759,7 +790,7 @@ function cmdPlanTable(opts) {
759
790
  const plan = buildPlanTable(raw, base);
760
791
  const problems = planTaskIdProblems(plan, opts.approve ? String(opts.root) : null);
761
792
  if (opts.approve && problems.length) {
762
- throw new Error(`plan-table --approve: không ghi kế hoạch — ${problems.join('; ')}. Mỗi bước cần task_id riêng, khác --root.`);
793
+ throw new Error(`plan-table --approve: không ghi kế hoạch — ${problems.join('; ')}. Mỗi bước cần task_id riêng (8 ký tự a-z0-9, lấy bằng \`dieu-phoi new-id\`), khác --root.`);
763
794
  }
764
795
  if (problems.length) process.stderr.write(`dieu-phoi plan-table: cảnh báo — ${problems.join('; ')} (cần sửa trước khi --approve)\n`);
765
796
  const rec = opts.approve ? recordApprovedPlan(ctx, String(opts.root), path.resolve(opts.file), plan) : null;
@@ -790,5 +821,5 @@ function cmdPlanTable(opts) {
790
821
 
791
822
  module.exports = {
792
823
  cmdSetup, cmdUninstall, cmdDoctor, cmdHook, cmdSync, cmdVerdict, cmdSteer, cmdStats, cmdRetro, cmdChoose, cmdCtx, cmdState, measureOrch,
793
- cmdPlanTable, buildPlanTable, recordApprovedPlan, planTaskIdProblems, criticalPath, resolveAgentId, AGENT_OUTCOMES, USER_OUTCOMES, VERIFY,
824
+ cmdPlanTable, cmdNewId, requireTaskId, buildPlanTable, recordApprovedPlan, planTaskIdProblems, criticalPath, resolveAgentId, AGENT_OUTCOMES, USER_OUTCOMES, VERIFY,
794
825
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kienntpixon/dieu-phoi",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Hook + CLI ghi log subagent của Claude Code và chọn model/effort theo chi phí + thời gian tới lúc nghiệm thu.",
5
5
  "license": "MIT",
6
6
  "author": "kiennt <kiennt@pixon.games>",