mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-26 17:09:14 +02:00
Compare commits
1 Commits
codex/agen
...
agent/j/eb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b24f628a1 |
@@ -285,19 +285,11 @@ If you need full isolation between organizations or accounts — separate tokens
|
||||
|
||||
```bash
|
||||
multica workspace list
|
||||
multica workspace list --full-id
|
||||
multica workspace list --output json
|
||||
```
|
||||
|
||||
The current default workspace is marked with `*`.
|
||||
|
||||
### Show Current Workspace
|
||||
|
||||
```bash
|
||||
multica workspace current
|
||||
multica workspace current --output json
|
||||
```
|
||||
|
||||
Prints the workspace that commands without `--workspace-id` and `MULTICA_WORKSPACE_ID` would target.
|
||||
The current default workspace is marked with `*`. Table output shows short UUID prefixes — pass `--full-id` when you need the canonical UUIDs.
|
||||
|
||||
### Switch Default Workspace
|
||||
|
||||
@@ -315,10 +307,12 @@ multica workspace get <workspace-id>
|
||||
multica workspace get <workspace-id> --output json
|
||||
```
|
||||
|
||||
Passing no `<workspace-id>` resolves to the current default workspace, so `multica workspace get` doubles as "what workspace am I on?".
|
||||
|
||||
### List Members
|
||||
|
||||
```bash
|
||||
multica workspace members <workspace-id>
|
||||
multica workspace member list <workspace-id>
|
||||
```
|
||||
|
||||
## Issues
|
||||
@@ -350,7 +344,7 @@ multica issue create --title "Fix login bug" --description "..." --priority high
|
||||
multica issue create --title "Fix login bug" --assignee-id 5fb87ac7-23b5-4a7a-81fa-ed295a54545d
|
||||
```
|
||||
|
||||
Flags: `--title` (required), `--description`, `--status`, `--priority`, `--assignee` / `--assignee-id`, `--parent`, `--project`, `--due-date`. Pass `--assignee-id <uuid>` (mutually exclusive with `--assignee`) when scripting against the IDs returned by `multica workspace members --output json` / `multica agent list --output json`.
|
||||
Flags: `--title` (required), `--description`, `--status`, `--priority`, `--assignee` / `--assignee-id`, `--parent`, `--project`, `--due-date`. Pass `--assignee-id <uuid>` (mutually exclusive with `--assignee`) when scripting against the IDs returned by `multica workspace member list --output json` / `multica agent list --output json`.
|
||||
|
||||
### Update Issue
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ multica issue assign MUL-42 --to alice
|
||||
multica issue assign MUL-42 --to-id 5fb87ac7-23b5-4a7a-81fa-ed295a54545d
|
||||
```
|
||||
|
||||
`--to` takes a member username or an agent name (fuzzy match). When names overlap — e.g. an agent `J` alongside `Cursor - J` — pass `--to-id <uuid>` instead, using the `user_id` (member) or `id` (agent) from `multica workspace members --output json` / `multica agent list --output json`. UUID matching is strict and unambiguous, which is what you want from scripts and from agents driving the CLI. `--to` and `--to-id` are mutually exclusive.
|
||||
`--to` takes a member username or an agent name (fuzzy match). When names overlap — e.g. an agent `J` alongside `Cursor - J` — pass `--to-id <uuid>` instead, using the `user_id` (member) or `id` (agent) from `multica workspace member list --output json` / `multica agent list --output json`. UUID matching is strict and unambiguous, which is what you want from scripts and from agents driving the CLI. `--to` and `--to-id` are mutually exclusive.
|
||||
|
||||
Unassign:
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ multica issue assign MUL-42 --to alice
|
||||
multica issue assign MUL-42 --to-id 5fb87ac7-23b5-4a7a-81fa-ed295a54545d
|
||||
```
|
||||
|
||||
`--to` 后跟成员用户名或智能体名字(模糊匹配)。如果工作区里有同名 / 互相含子串的成员或智能体(例如 agent `J` 旁边还有 `Cursor - J`),改用 `--to-id <uuid>`:UUID 来自 `multica workspace members --output json` 的 `user_id` 或 `multica agent list --output json` 的 `id`,是唯一精确的方式,特别适合脚本和驱动 CLI 的智能体。`--to` 和 `--to-id` 互斥。
|
||||
`--to` 后跟成员用户名或智能体名字(模糊匹配)。如果工作区里有同名 / 互相含子串的成员或智能体(例如 agent `J` 旁边还有 `Cursor - J`),改用 `--to-id <uuid>`:UUID 来自 `multica workspace member list --output json` 的 `user_id` 或 `multica agent list --output json` 的 `id`,是唯一精确的方式,特别适合脚本和驱动 CLI 的智能体。`--to` 和 `--to-id` 互斥。
|
||||
|
||||
取消分配:
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ For the difference between token types, see [Authentication and tokens](/auth-to
|
||||
|---|---|
|
||||
| `multica workspace list` | List every workspace you can access |
|
||||
| `multica workspace get <slug>` | Show details for one workspace |
|
||||
| `multica workspace members` | List members of the current workspace |
|
||||
| `multica workspace member list` | List members of the current workspace |
|
||||
| `multica workspace update <id> --name "..." [--description "..."] [--context "..."] [--issue-prefix "..."]` | Update workspace metadata (admin/owner). Long fields accept `--description-stdin` / `--context-stdin`. |
|
||||
|
||||
## Issues and projects
|
||||
|
||||
@@ -39,7 +39,7 @@ Token 类型的详细区分见 [认证与令牌](/auth-tokens)。
|
||||
|---|---|
|
||||
| `multica workspace list` | 列出你有权访问的所有工作区 |
|
||||
| `multica workspace get <slug>` | 查看一个工作区的详情 |
|
||||
| `multica workspace members` | 列出当前工作区的成员 |
|
||||
| `multica workspace member list` | 列出当前工作区的成员 |
|
||||
| `multica workspace update <id> --name "..." [--description "..."] [--context "..."] [--issue-prefix "..."]` | 修改 workspace 元数据(admin/owner 权限)。长文本可用 `--description-stdin` / `--context-stdin`。 |
|
||||
|
||||
## Issue 和 Project
|
||||
|
||||
@@ -210,7 +210,7 @@ multica workspace get <workspace-id> --output json
|
||||
### List Members
|
||||
|
||||
```bash
|
||||
multica workspace members <workspace-id>
|
||||
multica workspace member list <workspace-id>
|
||||
```
|
||||
|
||||
### Update Workspace
|
||||
@@ -267,7 +267,7 @@ multica issue create --title "Fix login bug" --description "..." --priority high
|
||||
multica issue create --title "Fix login bug" --assignee-id 5fb87ac7-23b5-4a7a-81fa-ed295a54545d
|
||||
```
|
||||
|
||||
Flags: `--title` (required), `--description`, `--status`, `--priority`, `--assignee` / `--assignee-id`, `--parent`, `--project`, `--due-date`. 脚本里如果已经拿到了 UUID(例如来自 `multica workspace members --output json`),传 `--assignee-id <uuid>`(与 `--assignee` 互斥)以精确锁定。
|
||||
Flags: `--title` (required), `--description`, `--status`, `--priority`, `--assignee` / `--assignee-id`, `--parent`, `--project`, `--due-date`. 脚本里如果已经拿到了 UUID(例如来自 `multica workspace member list --output json`),传 `--assignee-id <uuid>`(与 `--assignee` 互斥)以精确锁定。
|
||||
|
||||
### Update Issue
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ Assign the issue to the agent you just created — click its avatar in the web U
|
||||
multica issue assign MUL-1 --to my-agent-name
|
||||
```
|
||||
|
||||
`--to` takes the **name** of an agent or member. A substring match works — if the agent is called `my-code-reviewer`, `reviewer` resolves to it. If your workspace has overlapping names, pass `--to-id <uuid>` instead (mutually exclusive with `--to`); look up the UUID via `multica agent list --output json` or `multica workspace members --output json`.
|
||||
`--to` takes the **name** of an agent or member. A substring match works — if the agent is called `my-code-reviewer`, `reviewer` resolves to it. If your workspace has overlapping names, pass `--to-id <uuid>` instead (mutually exclusive with `--to`); look up the UUID via `multica agent list --output json` or `multica workspace member list --output json`.
|
||||
|
||||
**What happens next from the daemon**:
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ multica issue create --title "给 README 加一段 ASCII 架构图"
|
||||
multica issue assign MUL-1 --to my-agent-name
|
||||
```
|
||||
|
||||
`--to` 后面填智能体或成员的**名字**,子串就行——如果智能体叫 `my-code-reviewer`,填 `reviewer` 也能命中。如果工作区里名字相互重叠或冲突,改用 `--to-id <uuid>`(与 `--to` 互斥);UUID 来自 `multica agent list --output json` 或 `multica workspace members --output json`。
|
||||
`--to` 后面填智能体或成员的**名字**,子串就行——如果智能体叫 `my-code-reviewer`,填 `reviewer` 也能命中。如果工作区里名字相互重叠或冲突,改用 `--to-id <uuid>`(与 `--to` 互斥);UUID 来自 `multica agent list --output json` 或 `multica workspace member list --output json`。
|
||||
|
||||
**接下来守护进程会**:
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ There is currently no unarchive command; create a new squad if you need the rout
|
||||
| `multica squad member remove <id> --member-id <uuid> --type agent\|member` | Remove a member (the leader cannot be removed — change leader first) |
|
||||
| `multica squad activity <issue-id> <action\|no_action\|failed> --reason "..."` | Recorded by the leader agent at the end of every turn |
|
||||
|
||||
`--leader` accepts an agent name or UUID; for everything else, IDs come from `multica agent list --output json`, `multica workspace members --output json`, and `multica squad list --output json`.
|
||||
`--leader` accepts an agent name or UUID; for everything else, IDs come from `multica agent list --output json`, `multica workspace member list --output json`, and `multica squad list --output json`.
|
||||
|
||||
## Next
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ multica squad member add <squad-id> --member-id <agent-or-user-uuid> --type agen
|
||||
| `multica squad member remove <id> --member-id <uuid> --type agent\|member` | 移除成员(**不能移除队长**——先换队长)|
|
||||
| `multica squad activity <issue-id> <action\|no_action\|failed> --reason "..."` | 队长每次结束前由它自己调用 |
|
||||
|
||||
`--leader` 接受智能体名字或 UUID;其它 ID 从 `multica agent list --output json`、`multica workspace members --output json`、`multica squad list --output json` 拿。
|
||||
`--leader` 接受智能体名字或 UUID;其它 ID 从 `multica agent list --output json`、`multica workspace member list --output json`、`multica squad list --output json` 拿。
|
||||
|
||||
## 下一步
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@ func TestLegacyCompatibilityCommandsRemainAvailable(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("workspace members remains available", func(t *testing.T) {
|
||||
if _, _, err := workspaceCmd.Find([]string{"members"}); err != nil {
|
||||
t.Fatalf("expected workspace members command to exist: %v", err)
|
||||
t.Run("workspace member list remains available", func(t *testing.T) {
|
||||
if _, _, err := workspaceCmd.Find([]string{"member", "list"}); err != nil {
|
||||
t.Fatalf("expected workspace member list command to exist: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1664,7 +1664,7 @@ func ambiguousAssigneeError(input string, matches []assigneeMatch) error {
|
||||
// assignee_id) by looking it up against the workspace's members, agents, and
|
||||
// (when allowed) squads. It is the deterministic counterpart to
|
||||
// resolveAssignee: callers that already hold a UUID (e.g. agents reading IDs
|
||||
// from `multica workspace members --output json`) should use this instead of
|
||||
// from `multica workspace member list --output json`) should use this instead of
|
||||
// round-tripping through name matching, which can be ambiguous in workspaces
|
||||
// with overlapping names.
|
||||
func resolveAssigneeByID(ctx context.Context, client *cli.APIClient, id string, kinds assigneeKinds) (string, string, error) {
|
||||
|
||||
@@ -32,8 +32,13 @@ var workspaceGetCmd = &cobra.Command{
|
||||
RunE: runWorkspaceGet,
|
||||
}
|
||||
|
||||
var workspaceMembersCmd = &cobra.Command{
|
||||
Use: "members [workspace-id]",
|
||||
var workspaceMemberCmd = &cobra.Command{
|
||||
Use: "member",
|
||||
Short: "Manage workspace members",
|
||||
}
|
||||
|
||||
var workspaceMemberListCmd = &cobra.Command{
|
||||
Use: "list [workspace-id]",
|
||||
Short: "List workspace members",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: runWorkspaceMembers,
|
||||
@@ -60,24 +65,18 @@ var workspaceSwitchCmd = &cobra.Command{
|
||||
RunE: runWorkspaceSwitch,
|
||||
}
|
||||
|
||||
var workspaceCurrentCmd = &cobra.Command{
|
||||
Use: "current",
|
||||
Short: "Show the current default workspace",
|
||||
RunE: runWorkspaceCurrent,
|
||||
}
|
||||
|
||||
func init() {
|
||||
workspaceCmd.AddCommand(workspaceListCmd)
|
||||
workspaceCmd.AddCommand(workspaceGetCmd)
|
||||
workspaceCmd.AddCommand(workspaceMembersCmd)
|
||||
workspaceCmd.AddCommand(workspaceMemberCmd)
|
||||
workspaceMemberCmd.AddCommand(workspaceMemberListCmd)
|
||||
workspaceCmd.AddCommand(workspaceUpdateCmd)
|
||||
workspaceCmd.AddCommand(workspaceSwitchCmd)
|
||||
workspaceCmd.AddCommand(workspaceCurrentCmd)
|
||||
|
||||
workspaceListCmd.Flags().String("output", "table", "Output format: table or json")
|
||||
workspaceListCmd.Flags().Bool("full-id", false, "Show full UUIDs in table output")
|
||||
workspaceGetCmd.Flags().String("output", "json", "Output format: table or json")
|
||||
workspaceMembersCmd.Flags().String("output", "table", "Output format: table or json")
|
||||
workspaceCurrentCmd.Flags().String("output", "table", "Output format: table or json")
|
||||
workspaceMemberListCmd.Flags().String("output", "table", "Output format: table or json")
|
||||
|
||||
workspaceUpdateCmd.Flags().String("name", "", "New workspace name")
|
||||
workspaceUpdateCmd.Flags().String("description", "", "New description (decodes \\n, \\r, \\t, \\\\; pipe via --description-stdin to preserve literal backslashes)")
|
||||
@@ -89,7 +88,7 @@ func init() {
|
||||
}
|
||||
|
||||
// workspaceSummary is the subset of fields the CLI needs from /api/workspaces
|
||||
// to drive list/switch/current. Keeping it here (instead of using the full
|
||||
// to drive list and switch. Keeping it here (instead of using the full
|
||||
// WorkspaceResponse) avoids a dependency on the handler package.
|
||||
type workspaceSummary struct {
|
||||
ID string `json:"id"`
|
||||
@@ -98,8 +97,8 @@ type workspaceSummary struct {
|
||||
}
|
||||
|
||||
// fetchWorkspaces lists all workspaces the authenticated user belongs to. It
|
||||
// is shared by `list`, `switch`, and `current` so all three see the same
|
||||
// access-controlled view of workspaces.
|
||||
// is shared by `list` and `switch` so both see the same access-controlled view
|
||||
// of workspaces.
|
||||
func fetchWorkspaces(ctx context.Context, cmd *cobra.Command) ([]workspaceSummary, error) {
|
||||
serverURL := resolveServerURL(cmd)
|
||||
token := resolveToken(cmd)
|
||||
@@ -135,6 +134,7 @@ func runWorkspaceList(cmd *cobra.Command, _ []string) error {
|
||||
}
|
||||
|
||||
currentID := resolveWorkspaceID(cmd)
|
||||
fullID, _ := cmd.Flags().GetBool("full-id")
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
|
||||
fmt.Fprintln(w, "\tID\tNAME\tSLUG")
|
||||
for _, ws := range workspaces {
|
||||
@@ -142,7 +142,7 @@ func runWorkspaceList(cmd *cobra.Command, _ []string) error {
|
||||
if ws.ID == currentID {
|
||||
marker = "*"
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", marker, ws.ID, ws.Name, ws.Slug)
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", marker, displayID(ws.ID, fullID), ws.Name, ws.Slug)
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
return err
|
||||
@@ -209,41 +209,6 @@ func runWorkspaceSwitch(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func runWorkspaceCurrent(cmd *cobra.Command, _ []string) error {
|
||||
currentID := resolveWorkspaceID(cmd)
|
||||
if currentID == "" {
|
||||
return fmt.Errorf("no default workspace set: use 'multica workspace switch <id|slug>' to pick one")
|
||||
}
|
||||
|
||||
client, err := newAPIClient(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var ws map[string]any
|
||||
if err := client.GetJSON(ctx, "/api/workspaces/"+currentID, &ws); err != nil {
|
||||
return fmt.Errorf("get workspace: %w", err)
|
||||
}
|
||||
|
||||
output, _ := cmd.Flags().GetString("output")
|
||||
if output == "json" {
|
||||
return cli.PrintJSON(os.Stdout, ws)
|
||||
}
|
||||
|
||||
headers := []string{"ID", "NAME", "SLUG", "ISSUE PREFIX"}
|
||||
rows := [][]string{{
|
||||
strVal(ws, "id"),
|
||||
strVal(ws, "name"),
|
||||
strVal(ws, "slug"),
|
||||
strVal(ws, "issue_prefix"),
|
||||
}}
|
||||
cli.PrintTable(os.Stdout, headers, rows)
|
||||
return nil
|
||||
}
|
||||
|
||||
func workspaceIDFromArgs(cmd *cobra.Command, args []string) string {
|
||||
if len(args) > 0 {
|
||||
return args[0]
|
||||
|
||||
@@ -624,7 +624,7 @@ func TestInjectRuntimeConfigAvailableCommandsCoreOnly(t *testing.T) {
|
||||
"multica issue label list",
|
||||
"multica issue subscriber list",
|
||||
"multica label list",
|
||||
"multica workspace members",
|
||||
"multica workspace member list",
|
||||
"multica agent list",
|
||||
"multica squad list",
|
||||
"multica issue runs",
|
||||
|
||||
@@ -65,7 +65,7 @@ func buildQuickCreatePrompt(task Task) string {
|
||||
|
||||
// assignee
|
||||
b.WriteString("- **assignee**:\n")
|
||||
b.WriteString(" - When the user names someone (\"assign to X\" / \"@X\"), call `multica workspace members --output json`, `multica agent list --output json`, and `multica squad list --output json` and find the matching entity by display name. Squads are first-class assignees too — a squad name (e.g. \"Super Human\") routes work to the squad leader, who then delegates. On a clean unambiguous match, prefer `--assignee-id <uuid>` using the `user_id` (member) or `id` (agent or squad) from that JSON — UUID matching is exact and robust to name collisions in workspaces with overlapping names. `--assignee <name>` (fuzzy) is acceptable as a fallback when names are unambiguous. On no match or ambiguous match, do NOT pass either flag — instead append a final line to the description: `Unrecognized assignee: X`.\n")
|
||||
b.WriteString(" - When the user names someone (\"assign to X\" / \"@X\"), call `multica workspace member list --output json`, `multica agent list --output json`, and `multica squad list --output json` and find the matching entity by display name. Squads are first-class assignees too — a squad name (e.g. \"Super Human\") routes work to the squad leader, who then delegates. On a clean unambiguous match, prefer `--assignee-id <uuid>` using the `user_id` (member) or `id` (agent or squad) from that JSON — UUID matching is exact and robust to name collisions in workspaces with overlapping names. `--assignee <name>` (fuzzy) is acceptable as a fallback when names are unambiguous. On no match or ambiguous match, do NOT pass either flag — instead append a final line to the description: `Unrecognized assignee: X`.\n")
|
||||
b.WriteString(" - Treat bare @-routing as an assignee directive even when the user did not write the English word \"assign\". This includes Chinese imperatives like `让 @独立团 review 这个 PR`, `给 @X 处理`, or `交给 @X`; strip the leading `@`/`@` before matching display names. Do not keep that routing wrapper or `@Name` in the description unless it is a true CC-style notification rather than ownership. If the matched entity is a squad, pass the squad's `id` as `--assignee-id`, not the leader agent's id.\n")
|
||||
agentID := ""
|
||||
agentName := ""
|
||||
|
||||
Reference in New Issue
Block a user