@codex-modules/teams 0.1.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/dist/team.js ADDED
@@ -0,0 +1,175 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ export const TEAM_NAME_RE = /^[a-z][a-z0-9-]{0,40}$/;
3
+ export const MEMBER_NAME_RE = /^[a-z][a-z0-9-]{0,30}$/;
4
+ const SANDBOXES = new Set(["read-only", "workspace-write"]);
5
+ const LENSES = new Set(["area", "ownership", "perspective"]);
6
+ export function parseTeamJson(path) {
7
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
8
+ return validateTeamDef(parsed, path);
9
+ }
10
+ export function validateTeamDef(value, label = "team.json") {
11
+ const errors = collectTeamErrors(value);
12
+ if (errors.length > 0)
13
+ throw new Error(`${label} is invalid:\n- ${errors.join("\n- ")}`);
14
+ return value;
15
+ }
16
+ export function collectTeamErrors(value) {
17
+ const errors = [];
18
+ if (!isRecord(value))
19
+ return ["team definition must be an object"];
20
+ if (value.version !== 1)
21
+ errors.push("version must be 1");
22
+ if (typeof value.name !== "string" || !TEAM_NAME_RE.test(value.name)) {
23
+ errors.push("name must match ^[a-z][a-z0-9-]{0,40}$");
24
+ }
25
+ if (value.description !== undefined && typeof value.description !== "string") {
26
+ errors.push("description must be a string when present");
27
+ }
28
+ if (value.defaults !== undefined) {
29
+ if (!isRecord(value.defaults))
30
+ errors.push("defaults must be an object when present");
31
+ else {
32
+ if (value.defaults.model !== undefined && value.defaults.model !== null && !nonEmptyString(value.defaults.model)) {
33
+ errors.push("defaults.model must be a non-empty string when present");
34
+ }
35
+ if (value.defaults.sandbox_mode !== undefined &&
36
+ value.defaults.sandbox_mode !== null &&
37
+ !SANDBOXES.has(value.defaults.sandbox_mode)) {
38
+ errors.push("defaults.sandbox_mode must be read-only or workspace-write");
39
+ }
40
+ }
41
+ }
42
+ if (!Array.isArray(value.members)) {
43
+ errors.push("members must be an array");
44
+ return errors;
45
+ }
46
+ if (value.members.length < 2 || value.members.length > 8)
47
+ errors.push("members must contain 2 to 8 entries");
48
+ const seen = new Set();
49
+ value.members.forEach((member, index) => {
50
+ const prefix = `members[${index}]`;
51
+ if (!isRecord(member)) {
52
+ errors.push(`${prefix} must be an object`);
53
+ return;
54
+ }
55
+ if (typeof member.name !== "string" || !MEMBER_NAME_RE.test(member.name)) {
56
+ errors.push(`${prefix}.name must match ^[a-z][a-z0-9-]{0,30}$`);
57
+ }
58
+ else if (seen.has(member.name)) {
59
+ errors.push(`${prefix}.name duplicates ${member.name}`);
60
+ }
61
+ else {
62
+ seen.add(member.name);
63
+ }
64
+ if (!nonEmptyString(member.focus))
65
+ errors.push(`${prefix}.focus is required`);
66
+ if (!LENSES.has(String(member.lens)))
67
+ errors.push(`${prefix}.lens must be area, ownership, or perspective`);
68
+ if (!nonEmptyString(member.deliverable))
69
+ errors.push(`${prefix}.deliverable is required`);
70
+ if (member.model !== undefined && member.model !== null && !nonEmptyString(member.model)) {
71
+ errors.push(`${prefix}.model must be a non-empty string when present`);
72
+ }
73
+ if (member.sandbox_mode !== undefined &&
74
+ member.sandbox_mode !== null &&
75
+ !SANDBOXES.has(member.sandbox_mode)) {
76
+ errors.push(`${prefix}.sandbox_mode must be read-only or workspace-write`);
77
+ }
78
+ if (member.instructions !== undefined && member.instructions !== null && typeof member.instructions !== "string") {
79
+ errors.push(`${prefix}.instructions must be a string when present`);
80
+ }
81
+ });
82
+ return errors;
83
+ }
84
+ export function scaffoldTeam(preset = "review-panel") {
85
+ if (preset === "swarm") {
86
+ return {
87
+ version: 1,
88
+ name: "swarm",
89
+ description: "Parallel exploration team",
90
+ defaults: { model: "gpt-5.4-mini", sandbox_mode: "read-only" },
91
+ members: [1, 2, 3].map(n => ({
92
+ name: `explorer-${n}`,
93
+ focus: `independent exploration slice ${n}`,
94
+ lens: "area",
95
+ deliverable: "concise evidence-backed notes with file references",
96
+ })),
97
+ };
98
+ }
99
+ if (preset === "pipeline") {
100
+ return {
101
+ version: 1,
102
+ name: "pipeline",
103
+ description: "Builder and verifier gated workflow",
104
+ defaults: { model: "gpt-5.4-mini", sandbox_mode: "workspace-write" },
105
+ members: [
106
+ {
107
+ name: "builder",
108
+ focus: "smallest implementation that satisfies the requested contract",
109
+ lens: "ownership",
110
+ deliverable: "changed files and verification notes",
111
+ },
112
+ {
113
+ name: "verifier",
114
+ focus: "behavioral regressions, missing tests, and contract drift",
115
+ lens: "perspective",
116
+ deliverable: "pass/fail findings with exact reproduction steps",
117
+ sandbox_mode: "read-only",
118
+ },
119
+ ],
120
+ };
121
+ }
122
+ return {
123
+ version: 1,
124
+ name: "review-panel",
125
+ description: "Adversarial review panel",
126
+ defaults: { model: "gpt-5.4-mini", sandbox_mode: "read-only" },
127
+ members: [
128
+ {
129
+ name: "security",
130
+ focus: "auth, injection, filesystem, and permission surfaces of the change",
131
+ lens: "perspective",
132
+ deliverable: "findings list with file:line, severity, and exploit path",
133
+ },
134
+ {
135
+ name: "correctness",
136
+ focus: "runtime behavior, edge cases, and data contract mismatches",
137
+ lens: "area",
138
+ deliverable: "reproducible bugs with expected versus actual behavior",
139
+ },
140
+ {
141
+ name: "simplicity",
142
+ focus: "unnecessary abstractions, ownership confusion, and deletion opportunities",
143
+ lens: "ownership",
144
+ deliverable: "specific simplifications and the files they affect",
145
+ },
146
+ ],
147
+ };
148
+ }
149
+ export function writePreset(path, preset) {
150
+ writeFileSync(path, `${JSON.stringify(scaffoldTeam(preset), null, 2)}\n`, { mode: 0o600 });
151
+ }
152
+ export function memberAgentName(team, member) {
153
+ const teamName = typeof team === "string" ? team : team.name;
154
+ const memberName = typeof member === "string" ? member : member.name;
155
+ if (!TEAM_NAME_RE.test(teamName))
156
+ throw new Error(`invalid team name: ${teamName}`);
157
+ if (!MEMBER_NAME_RE.test(memberName))
158
+ throw new Error(`invalid member name: ${memberName}`);
159
+ return `${teamName}-${memberName}`;
160
+ }
161
+ export function resolveMemberModel(team, member) {
162
+ const model = member.model ?? team.defaults?.model;
163
+ if (!model)
164
+ throw new Error(`member ${member.name} has no model and defaults.model is not set`);
165
+ return model;
166
+ }
167
+ export function resolveMemberSandbox(team, member) {
168
+ return member.sandbox_mode ?? team.defaults?.sandbox_mode ?? "read-only";
169
+ }
170
+ function isRecord(value) {
171
+ return typeof value === "object" && value !== null && !Array.isArray(value);
172
+ }
173
+ function nonEmptyString(value) {
174
+ return typeof value === "string" && value.trim().length > 0;
175
+ }
package/dist/toml.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export type TomlPrimitive = string | string[];
2
+ export declare function renderToml(values: Record<string, TomlPrimitive>): string;
3
+ export declare function tomlBasicString(value: string): string;
package/dist/toml.js ADDED
@@ -0,0 +1,38 @@
1
+ export function renderToml(values) {
2
+ return `${Object.entries(values)
3
+ .map(([key, value]) => `${assertSafeKey(key)} = ${renderValue(value)}`)
4
+ .join("\n")}\n`;
5
+ }
6
+ export function tomlBasicString(value) {
7
+ let rendered = '"';
8
+ for (const char of value.replace(/\r\n/g, "\n").replace(/\r/g, "\n")) {
9
+ const code = char.charCodeAt(0);
10
+ if (char === "\b")
11
+ rendered += "\\b";
12
+ else if (char === "\t")
13
+ rendered += "\\t";
14
+ else if (char === "\n")
15
+ rendered += "\\n";
16
+ else if (char === "\f")
17
+ rendered += "\\f";
18
+ else if (char === '"')
19
+ rendered += '\\"';
20
+ else if (char === "\\")
21
+ rendered += "\\\\";
22
+ else if (code === 0x7f || code < 0x20)
23
+ rendered += `\\u${code.toString(16).padStart(4, "0")}`;
24
+ else
25
+ rendered += char;
26
+ }
27
+ return `${rendered}"`;
28
+ }
29
+ function renderValue(value) {
30
+ if (Array.isArray(value))
31
+ return `[${value.map(tomlBasicString).join(", ")}]`;
32
+ return tomlBasicString(value);
33
+ }
34
+ function assertSafeKey(key) {
35
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
36
+ throw new Error(`unsafe TOML key: ${key}`);
37
+ return key;
38
+ }
@@ -0,0 +1,114 @@
1
+ export type SandboxMode = "read-only" | "workspace-write";
2
+ export type MemberLens = "area" | "ownership" | "perspective";
3
+ export type InstallScope = "user" | "project";
4
+ export type TeamStatus = "active" | "finished";
5
+ export type FinishStatus = "ok" | "partial";
6
+ export type TaskStatus = "open" | "claimed" | "done" | "failed";
7
+ export type JournalKind = "note" | "handoff" | "decision";
8
+ export type TeamDefaults = {
9
+ model?: string | null;
10
+ sandbox_mode?: SandboxMode | null;
11
+ };
12
+ export type MemberDef = {
13
+ name: string;
14
+ focus: string;
15
+ lens: MemberLens;
16
+ deliverable: string;
17
+ model?: string | null;
18
+ sandbox_mode?: SandboxMode | null;
19
+ instructions?: string | null;
20
+ };
21
+ export type TeamDef = {
22
+ version: 1;
23
+ name: string;
24
+ description?: string;
25
+ defaults?: TeamDefaults;
26
+ members: MemberDef[];
27
+ };
28
+ export type BoundMember = {
29
+ agent_id: string;
30
+ nickname?: string;
31
+ bound_at: string;
32
+ };
33
+ export type StateFile = {
34
+ version: 1;
35
+ team: string;
36
+ goal: string;
37
+ status: TeamStatus;
38
+ leader_session?: string;
39
+ members: Record<string, BoundMember>;
40
+ created_at: string;
41
+ finished_at?: string;
42
+ finish_status?: FinishStatus;
43
+ };
44
+ export type TaskRecord = {
45
+ id: string;
46
+ title: string;
47
+ detail?: string;
48
+ depends_on: string[];
49
+ status: TaskStatus;
50
+ claimed_by?: string;
51
+ lease_expires_at?: string;
52
+ result?: string;
53
+ created_at: string;
54
+ updated_at: string;
55
+ };
56
+ export type TasksFile = {
57
+ version: 1;
58
+ tasks: TaskRecord[];
59
+ };
60
+ export type JournalEntry = {
61
+ ts: string;
62
+ actor: string;
63
+ kind: JournalKind;
64
+ text: string;
65
+ };
66
+ export type ManifestEntry = {
67
+ team: string;
68
+ scope: InstallScope | "skill";
69
+ file: string;
70
+ backup?: string | null;
71
+ hash: string;
72
+ kind: "agent" | "skill";
73
+ member?: string;
74
+ installed_at: string;
75
+ };
76
+ export type TeamsManifest = {
77
+ version: 1;
78
+ owner: "@codex-modules/teams";
79
+ entries: ManifestEntry[];
80
+ };
81
+ export type DoctorReport = {
82
+ codexBinary: string | null;
83
+ version: string | null;
84
+ features: Array<{
85
+ name: string;
86
+ stage: string;
87
+ enabled: boolean;
88
+ }>;
89
+ featuresError?: string;
90
+ multiAgent: {
91
+ present: boolean;
92
+ stage?: string;
93
+ enabled?: boolean;
94
+ };
95
+ fanout?: {
96
+ present: boolean;
97
+ stage?: string;
98
+ enabled?: boolean;
99
+ };
100
+ multiAgentV2?: {
101
+ present: boolean;
102
+ stage?: string;
103
+ enabled?: boolean;
104
+ };
105
+ models: {
106
+ ok: boolean;
107
+ values: string[];
108
+ error?: string;
109
+ };
110
+ agentsDirWritable: boolean;
111
+ stateDirWritable: boolean;
112
+ userInstalledTeams: string[];
113
+ projectInstalledTeams: string[];
114
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@codex-modules/teams",
3
+ "version": "0.1.1",
4
+ "description": "Team definitions, durable state, and leader prompts for Codex native multi-agent collaboration.",
5
+ "keywords": [
6
+ "codex",
7
+ "openai-codex",
8
+ "codex-cli",
9
+ "teams",
10
+ "multi-agent",
11
+ "collaboration"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "ArcSolver <hailo.dev01@gmail.com>",
15
+ "homepage": "https://github.com/ArcSolver/codex-modules/tree/main/modules/teams#readme",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/ArcSolver/codex-modules.git",
19
+ "directory": "modules/teams"
20
+ },
21
+ "bugs": "https://github.com/ArcSolver/codex-modules/issues",
22
+ "type": "module",
23
+ "bin": {
24
+ "codex-teams": "dist/cli.js"
25
+ },
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js"
30
+ }
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "README.md",
35
+ "README.ko.md"
36
+ ],
37
+ "engines": {
38
+ "node": ">=24.0.0"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "scripts": {
44
+ "build": "tsc -p tsconfig.json",
45
+ "verify": "bash verify/verify.sh",
46
+ "prepublishOnly": "npm run build"
47
+ },
48
+ "dependencies": {},
49
+ "devDependencies": {
50
+ "@types/node": "^24.0.0",
51
+ "typescript": "^5.8.3"
52
+ }
53
+ }