From ebb3700106a620758054b4797300b8981e9e773b Mon Sep 17 00:00:00 2001 From: 8syncdev Date: Thu, 22 Oct 2026 07:47:49 +0700 Subject: [PATCH] =?UTF-8?q?feat(hq):=20"C=C3=B4ng=20ty"=20switch=20+=20`8s?= =?UTF-8?q?ync=20ccc=20auto=20company=20on|off`=20(v0.110.44)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows v0.110.40: the one-team-for-all-projects flag gets a verb and a switch next to "Critic trả lời ngay" in the office; the auto status line says so. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 4 ++++ Cargo.lock | 4 ++-- Cargo.toml | 2 +- apps/hq-ui/components/hq/office-view.tsx | 18 +++++++++++++++++- apps/hq-ui/lib/auto.ts | 5 ++++- apps/hq-ui/lib/server/auto.ts | 4 +++- apps/hq-ui/tests/auto-ui.test.ts | 1 + assets/claude/.claude-plugin/plugin.json | 2 +- crates/cli/src/verbs/ccc/auto.rs | 23 ++++++++++++++++++++++- crates/cli/src/verbs/ccc/help.rs | 1 + 10 files changed, 56 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f2640d8..9d46e527 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ versioning theo [SemVer](https://semver.org). **8sync rule:** mỗi PR cập nh ## [Unreleased] +## [0.110.44] — 2026-10-22 +### Added +- **Công tắc "Công ty" trên HQ + `8sync ccc auto company on|off`** (tiếp bản .40, owner 2026-10-22: "1 cty rooms/roles agent nhận nhiều dự án"): nút cạnh "Critic trả lời ngay" ở Văn phòng bật/tắt chế độ một team làm mọi dự án trong scope; dòng trạng thái auto ghi "Công ty: một team, mọi dự án"; `auto status --json` có `company` (8sync cũ hơn không hiện nút). Test auto-ui +1. + ## [0.110.43] — 2026-10-22 ### Added - **HQ tab "3D": văn phòng WebGL** (owner 2026-10-22: "ui ux … 3d nhanh nhẹ đẹp"; quyết định 2026-10-22: WebGL/three trong HQ hiện tại trước, Rust/Tauri native để sau). Mỗi dự án một sàn, mỗi phòng một bàn + một nhân vật tô màu theo trạng thái (Chờ anh = cam và thở nhẹ, đang làm = xanh và gật nhẹ, rảnh = xám); rê chuột hiện tên/dự án/trạng thái, bấm mở phòng; chọn dự án ở sidebar thì chỉ sàn đó. Nhẹ: three.js nạp động khi mở tab (≤ 417 KB gzip toàn bộ thư viện, chunk thật nhỏ hơn sau tree-shake — not verified: chưa đo build), không bóng, không post-processing, `powerPreference: low-power`, vòng vẽ chỉ chạy khi tab mở, `prefers-reduced-motion` tắt cử động; chân tab in số đo thật (số phòng, draw call, ms/khung). Layout thuần (`lib/office-3d.ts`) có test 2. diff --git a/Cargo.lock b/Cargo.lock index fc1764b3..a8c90b4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -200,7 +200,7 @@ dependencies = [ [[package]] name = "ccc-core" -version = "0.110.43" +version = "0.110.44" dependencies = [ "anyhow", "dirs", @@ -1503,7 +1503,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "su-code" -version = "0.110.43" +version = "0.110.44" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index bc651978..fff20df9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["crates/cli", "crates/ccc-core"] [workspace.package] -version = "0.110.43" +version = "0.110.44" edition = "2021" license = "PolyForm-Noncommercial-1.0.0" authors = ["8sync-dev <8sync.dev.1111@gmail.com>"] diff --git a/apps/hq-ui/components/hq/office-view.tsx b/apps/hq-ui/components/hq/office-view.tsx index e3d0d63a..61d382fb 100644 --- a/apps/hq-ui/components/hq/office-view.tsx +++ b/apps/hq-ui/components/hq/office-view.tsx @@ -1,6 +1,6 @@ "use client"; -import { BotIcon, GitBranchIcon, GlobeIcon, InboxIcon, LaptopIcon, MoonIcon, PauseIcon, PlayIcon, RefreshCwIcon, SparklesIcon, TerminalSquareIcon, UsersIcon } from "lucide-react"; +import { BotIcon, Building2Icon, GitBranchIcon, GlobeIcon, InboxIcon, LaptopIcon, MoonIcon, PauseIcon, PlayIcon, RefreshCwIcon, SparklesIcon, TerminalSquareIcon, UsersIcon } from "lucide-react"; import { useMemo, useState, type CSSProperties } from "react"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; @@ -385,6 +385,22 @@ function AutoBar({ token }: { token: string }) { Critic trả lời ngay: {status.delegate ? "bật" : "tắt"} )} + {/* "Công ty": one team for every project in scope (owner 2026-10-22: "1 cty rooms/roles agent nhận + nhiều dự án"); `auto company on|off`. Older 8sync: no `company` in the status, no switch. */} + {!status.error && typeof status.company === "boolean" && ( + + )} {/* "Lease liên tục": absent on an older 8sync (no `lease_auto` in the status), so no switch there. */} {!status.error && typeof status.lease_auto === "boolean" && ( <> diff --git a/apps/hq-ui/lib/auto.ts b/apps/hq-ui/lib/auto.ts index 616ae9ba..9e87172c 100644 --- a/apps/hq-ui/lib/auto.ts +++ b/apps/hq-ui/lib/auto.ts @@ -16,6 +16,8 @@ export type AutoState = { versions?: { ui: string | null; binary: string | null }; /** Teams auto drives (`auto.json` `teams`, contract C4); absent = ["ccc"]. */ teams?: string[]; + /** One team for every project in scope (`auto company on`); absent on an older 8sync. */ + company?: boolean; /** Why 8sync keeps the omp team off (omp on the anthropic provider); null when not blocked. */ omp_blocked?: string | null; /** The last "Đội omp" switch failed (8sync's reason). */ @@ -109,7 +111,8 @@ export function autoText(s: AutoState, now = Date.now()): string { : s.ci === "local" ? " · CI: máy release (GitHub chỉ lưu code)" : ""; - return `${t ? `Luôn chạy · lease tới ${hhmm(t)}` : "Luôn chạy"}${ci}`; + const company = s.company ? " · Công ty: một team, mọi dự án" : ""; + return `${t ? `Luôn chạy · lease tới ${hhmm(t)}` : "Luôn chạy"}${company}${ci}`; } if (s.manual) return "Idle/delegate bật tay — Chạy liên tục sẽ ghi đè"; return "Đang tắt"; diff --git a/apps/hq-ui/lib/server/auto.ts b/apps/hq-ui/lib/server/auto.ts index ece0e994..b7db9dde 100644 --- a/apps/hq-ui/lib/server/auto.ts +++ b/apps/hq-ui/lib/server/auto.ts @@ -43,7 +43,7 @@ export type AutoStatus = { lease_error?: string; }; -export const AUTO_ACTIONS = ["on", "pause", "omp-on", "omp-off", "lease-auto-on", "lease-auto-off", "critic-on", "critic-off"] as const; +export const AUTO_ACTIONS = ["on", "pause", "omp-on", "omp-off", "lease-auto-on", "lease-auto-off", "critic-on", "critic-off", "company-on", "company-off"] as const; export type AutoAction = (typeof AUTO_ACTIONS)[number]; /** The only argv each action may run. */ @@ -56,6 +56,8 @@ const ARGV: Record = { "lease-auto-off": ["ccc", "auto", "lease", "auto", "off"], "critic-on": ["ccc", "delegate", "on"], "critic-off": ["ccc", "delegate", "off"], + "company-on": ["ccc", "auto", "company", "on"], + "company-off": ["ccc", "auto", "company", "off"], }; const TIMEOUT_MS = 5000; diff --git a/apps/hq-ui/tests/auto-ui.test.ts b/apps/hq-ui/tests/auto-ui.test.ts index a59944e6..0c67bde3 100644 --- a/apps/hq-ui/tests/auto-ui.test.ts +++ b/apps/hq-ui/tests/auto-ui.test.ts @@ -55,6 +55,7 @@ describe("auto control model", () => { expect(shouldAutoStart(s)).toBe(false); expect(autoText({ on: true, paused: false, gate: "chạm trần $5/giờ", lease_until: past + 7200 })).toBe("Đang nghỉ: chạm trần $5/giờ"); expect(autoText({ on: false, paused: false, manual: true })).toBe("Idle/delegate bật tay — Chạy liên tục sẽ ghi đè"); + expect(autoText({ on: true, paused: false, company: true })).toBe("Luôn chạy · Công ty: một team, mọi dự án"); expect(autoText({ on: false, paused: false, error: "HTTP 401" })).toBe("Không đọc được trạng thái auto: HTTP 401"); expect(autoText({ on: false, paused: false, error: "không rõ `auto`" })).toBe("8sync chưa có lệnh auto — chạy 8sync up"); }); diff --git a/assets/claude/.claude-plugin/plugin.json b/assets/claude/.claude-plugin/plugin.json index be166c1d..c99ce994 100644 --- a/assets/claude/.claude-plugin/plugin.json +++ b/assets/claude/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "8sync-kit", "displayName": "8sync kit", - "version": "0.110.43", + "version": "0.110.44", "description": "8 Sync kit for Claude Code: role-routed subagents (explore, task, quick-task, reviewer, designer, researcher, verifier, pm, architect, qa, critic) and the /ship /rules /sec /harden skills. Hooks, rules and the full skill set come from `8sync ccc kit`.", "author": { "name": "8 Sync", "url": "https://8syncdev.com" }, "homepage": "https://get.8syncdev.com", diff --git a/crates/cli/src/verbs/ccc/auto.rs b/crates/cli/src/verbs/ccc/auto.rs index 8013f878..390d3a64 100644 --- a/crates/cli/src/verbs/ccc/auto.rs +++ b/crates/cli/src/verbs/ccc/auto.rs @@ -48,7 +48,7 @@ use serde_json::{json, Value}; use super::{delegate, idle, target}; -const USAGE: &str = "usage: 8sync ccc auto on [--here|--all] [--company] [--omp] [--loop [30m]] [--team] [--target \"<đích>\" [--check \"\"]] [--hours N] [--after ] [--usd-per-hour X] | off | pause | status [--json] | ci [local|github] [--json] +const USAGE: &str = "usage: 8sync ccc auto on [--here|--all] [--company] [--omp] [--loop [30m]] [--team] [--target \"<đích>\" [--check \"\"]] [--hours N] [--after ] [--usd-per-hour X] | off | pause | status [--json] | company on|off [--json] | ci [local|github] [--json] 8sync ccc auto omp on [--model ]|off [--json] | nudge --session --cwd [--model ] [--json] (on --omp nhận thêm --omp-model : model của đội omp) 8sync ccc auto up [--roles lead,dev,qa,review,rd] [--loop 30m] [--hours N] [--usd-per-hour X] [--clear-ss] [--omp [--omp-model ]] | up --add | down [--only ] @@ -2224,6 +2224,27 @@ pub(super) fn run( } return Ok(()); } + // `auto company on|off`: one team for every project in scope (HQ "Công ty" switch; [`set_company`]). + if rest.first().map(String::as_str) == Some("company") { + let on = match rest.get(1).map(String::as_str) { + Some("on") => true, + Some("off") => false, + _ => bail!("cần `on` hoặc `off` +{USAGE}"), + }; + if let Some(x) = rest.iter().skip(2).find(|a| *a != "--json") { + bail!("không rõ `{x}` +{USAGE}"); + } + set_company(home, on, now)?; + let v = status(home, subscription, now); + if rest.iter().any(|a| a == "--json") { + println!("{v}"); + } else { + crate::ui::ok(&format!("công ty: {} — một team làm {} dự án trong scope", if on { "bật" } else { "tắt" }, v["repos"].as_array().map_or(0, |r| r.len()))); + } + return Ok(()); + } if rest.first().map(String::as_str) == Some("omp") { let on = match rest.get(1).map(String::as_str) { Some("on") => true, diff --git a/crates/cli/src/verbs/ccc/help.rs b/crates/cli/src/verbs/ccc/help.rs index bf1560fc..777ee298 100644 --- a/crates/cli/src/verbs/ccc/help.rs +++ b/crates/cli/src/verbs/ccc/help.rs @@ -63,6 +63,7 @@ TỰ ĐỘNG (gõ trong Claude Code) 8sync ccc auto add \"\" [--check \"\"] [--label bug|feature|qa] [--role ] [--prio P0..P3] [--now] | queue | done --evidence \"\" | drop | reopen [--note \"…\"] hàng đích của dự án: mỗi nhịp loop các phòng làm phần hợp vai cho đích đầu hàng, đạt thì `done` rồi sang đích kế (evidence phải có `: ` hoặc SHA commit; \"chưa\", \"ok\" bị từ chối); `reopen` = mở lại card đóng nhầm về cột cũ, giữ lịch sử, ghi ai mở; `--now` = lên đầu hàng + vào inbox mọi phòng ngay (như ctrl+x ctrl+s); trong phòng nói \"đưa vào loop: …\" là đủ 8sync ccc auto board | lint | claim | wait | move [--role ] | prio | note \"…\" [--json] board kiểu super-board: backlog → ready → building → qa → review → approved (chờ Ship) → done, blocked; phòng `claim` card gấp nhất của cột mình (P0 trước, chưa đặt = P2, cùng mức theo thứ tự hàng) (lead/ba backlog, dev ready, qa qa, review review), cột ready chỉ giao card đúng vai phòng hoặc card không gắn vai (vai = `--role` hoặc tag đầu tiêu đề `[ui]`, `[dev]`, `[rd]`…; dev, dev2, backend, core cùng vai dev; card `[qa]` sang cột qa; tag chữ hay chữ-gạch-nối lạ như `[art-director]` cũng là vai, `[#125]`, `[hot-fix]`, `[wip]`, `[bug]`, `[p1]` thì không), card có vai mà dự án không có phòng không giao cho ai và `lint` báo cho lead, cột trống thì `wait` (chặn, không tốn token, tới khi có card; không `--timeout` thì chờ theo BASH_MAX_TIMEOUT_MS của phòng: ≤ 30 phút, không có thì 9 phút; phòng mở bằng `auto up` chạy trên /goal của Claude Code nên không ngồi rảnh), `move` chuyển cột có luật (ready cần --check, approved cần --sha; vào qa/approved thì PR phải mở, base main, không conflict, approved đúng head SHA, kiểm bằng `gh pr view`; mergeable UNKNOWN thì đọc lại 3 lần cách 3 s rồi báo GitHub đang tính, không tính lần bị trả; gh lỗi thì từ chối), bị trả về quá 2 lần → blocked, claim im 2 giờ → thu hồi 8sync ccc auto merge [--as ] [--json] lead chạy đầu mỗi nhịp: code (không tốn token) merge mọi card approved theo thứ tự board — `gh pr view` → base main (base đã merge thì `gh pr edit --base main`), MERGEABLE, head = SHA review duyệt → `gh pr merge --squash --delete-branch --match-head-commit ` → done `merged `; conflict, SHA đổi, base khác → về ready kèm lý do (tính một lần bị trả); mergeable UNKNOWN (GitHub đang tính sau push) → chờ, không phải conflict; cần quyền tự merge của owner cho dự án (8 giờ, giữ qua relaunch: câu `Ship` được duyệt hoặc câu trả lời bắt đầu \"Tự merge\"), thiếu thì in sẵn MỘT câu AskUserQuestion Ship (mọi card approved + PR) để lead hỏi ngay lượt đó, trước `auto wait`, và exit 1; khi có quyền, guard cũng cho `gh pr merge --match-head-commit ` đúng card approved, base main; `auto wait` của lead thức ngay khi có card approved mới + 8sync ccc auto company on|off [--json] một team làm mọi dự án trong scope (cờ `company` trong auto.json, giữ qua on/off/pause): phòng hết card ở dự án mình thì claim/wait lấy card gấp nhất ở dự án khác; HQ có công tắc \"Công ty\" cạnh \"Critic trả lời ngay\" 8sync ccc auto omp on [--model ]|off [--json] đội omp (phòng `8sync . `) chạy cạnh đội Claude Code: chỉ đổi `teams` (và `omp_model` khi có --model) trong auto.json, giữ mọi thứ khác; `--model /` = model của đội (phòng mở bằng `8sync . omp- --model `; giữ khi on/off không có --model; `on --omp`/`up --omp` nhận `--omp-model `); mỗi phòng omp được nhắc đích đầu hàng ≤ 1 lần/10 phút, ≤ 6 lần/giờ; tắt khi model đội (không đặt = model mặc định của omp) là anthropic/… (subscription Claude chỉ dùng trong Claude Code) 8sync ccc auto nudge --session --cwd [--model ] [--json] (extension omp gọi, --model = model đang chạy của phòng; không có → xét model mặc định của omp) {\"text\"}: lời nhắc đích hiện tại cho phòng omp, null khi auto tắt/tạm dừng, đội omp tắt, phòng chạy anthropic/…, ngoài --here, lease hết, hàng trống hoặc vừa nhắc 8sync ccc know add \"\" [--repo ] | ls [--all] [--json] cây tri thức chung của máy: mỗi repo một file `~/.local/state/8sync/ccc/know/.md` (dòng `- [] `); SessionStart nạp bài học của dự án này trước rồi 3 dòng cuối của từng dự án khác dưới tiêu đề root của nó (đọc để học, không lẫn); su-code/KNOWLEDGE.md vẫn là memory commit của repo; CHỈ ghi bài học chung (công cụ, hook, guard, cách làm) — chuyện riêng của khách/dự án, secret, dữ liệu khách KHÔNG vào cây chung (quy tắc tách từng client)