Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 <[email protected]>"]
Expand Down
18 changes: 17 additions & 1 deletion apps/hq-ui/components/hq/office-view.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -385,6 +385,22 @@ function AutoBar({ token }: { token: string }) {
Critic trả lời ngay: {status.delegate ? "bật" : "tắt"}
</Button>
)}
{/* "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" && (
<Button
type="button"
size="sm"
variant={status.company ? "secondary" : "outline"}
aria-pressed={status.company}
disabled={busy}
title="Một team làm mọi dự án trong scope: phòng hết card ở dự án mình thì lấy card gấp nhất ở dự án khác (8sync ccc auto company on|off). Board, inbox, Ship vẫn theo từng repo."
onClick={() => void setAuto(status.company ? "company-off" : "company-on")}
>
<Building2Icon aria-hidden />
Công ty: {status.company ? "bật" : "tắt"}
</Button>
)}
{/* "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" && (
<>
Expand Down
5 changes: 4 additions & 1 deletion apps/hq-ui/lib/auto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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). */
Expand Down Expand Up @@ -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";
Expand Down
4 changes: 3 additions & 1 deletion apps/hq-ui/lib/server/auto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -56,6 +56,8 @@ const ARGV: Record<AutoAction, string[]> = {
"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;
Expand Down
1 change: 1 addition & 0 deletions apps/hq-ui/tests/auto-ui.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});
Expand Down
2 changes: 1 addition & 1 deletion assets/claude/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
23 changes: 22 additions & 1 deletion crates/cli/src/verbs/ccc/auto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 \"<lệnh>\"]] [--hours N] [--after <giây|1m>] [--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 \"<lệnh>\"]] [--hours N] [--after <giây|1m>] [--usd-per-hour X] | off | pause | status [--json] | company on|off [--json] | ci [local|github] [--json]
8sync ccc auto omp on [--model <provider/model>]|off [--json] | nudge --session <room id> --cwd <dir> [--model <provider/model>] [--json]
(on --omp nhận thêm --omp-model <provider/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 <provider/model>]] | up --add <phòng> | down [--only <phòng>]
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions crates/cli/src/verbs/ccc/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ TỰ ĐỘNG (gõ trong Claude Code)
8sync ccc auto add \"<việc>\" [--check \"<lệnh>\"] [--label bug|feature|qa] [--role <vai>] [--prio P0..P3] [--now] | queue | done <id> --evidence \"<lệnh: kết quả>\" | drop <id> | reopen <id> [--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ó `<lệnh>: <kết quả>` 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 <id> <cột> [--role <vai>] | prio <id> <P0..P3|none> | note <id> \"…\" [--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 <phòng>] [--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 <sha>` → done `merged <sha>`; 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 <pr> --match-head-commit <sha>` đú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 <provider/model>]|off [--json] đội omp (phòng `8sync . <tên>`) 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 <provider>/<model>` = model của đội (phòng mở bằng `8sync . omp-<vai> --model <m>`; giữ khi on/off không có --model; `on --omp`/`up --omp` nhận `--omp-model <m>`); 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 <room id> --cwd <dir> [--model <provider/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 \"<bài học>\" [--repo <root>] | ls [--all] [--json] cây tri thức chung của máy: mỗi repo một file `~/.local/state/8sync/ccc/know/<repo>.md` (dòng `- <ngày> [<phòng>] <bài học>`); 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)
Expand Down
Loading