team 0.0.1 → 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/LICENSE +21 -0
- package/README.md +310 -0
- package/dist/approve/approval.d.ts +38 -0
- package/dist/approve/approval.js +78 -0
- package/dist/approve/diff.d.ts +13 -0
- package/dist/approve/diff.js +47 -0
- package/dist/approve/fingerprint.d.ts +44 -0
- package/dist/approve/fingerprint.js +109 -0
- package/dist/args.d.ts +7 -0
- package/dist/args.js +22 -0
- package/dist/budgets/checks.d.ts +35 -0
- package/dist/budgets/checks.js +77 -0
- package/dist/budgets/gate.d.ts +24 -0
- package/dist/budgets/gate.js +127 -0
- package/dist/budgets/readings.d.ts +69 -0
- package/dist/budgets/readings.js +156 -0
- package/dist/budgets/run.d.ts +66 -0
- package/dist/budgets/run.js +119 -0
- package/dist/budgets/table.d.ts +24 -0
- package/dist/budgets/table.js +90 -0
- package/dist/caller.d.ts +48 -0
- package/dist/caller.js +126 -0
- package/dist/check/config.d.ts +51 -0
- package/dist/check/config.js +50 -0
- package/dist/check/git.d.ts +27 -0
- package/dist/check/git.js +97 -0
- package/dist/check/message.d.ts +27 -0
- package/dist/check/message.js +101 -0
- package/dist/check/run.d.ts +31 -0
- package/dist/check/run.js +77 -0
- package/dist/check/signature.d.ts +10 -0
- package/dist/check/signature.js +22 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +76 -0
- package/dist/clis.d.ts +7 -0
- package/dist/clis.js +9 -0
- package/dist/commands/add.d.ts +23 -0
- package/dist/commands/add.js +445 -0
- package/dist/commands/approve.d.ts +11 -0
- package/dist/commands/approve.js +146 -0
- package/dist/commands/check.d.ts +17 -0
- package/dist/commands/check.js +103 -0
- package/dist/commands/doctor.d.ts +38 -0
- package/dist/commands/doctor.js +232 -0
- package/dist/commands/down.d.ts +37 -0
- package/dist/commands/down.js +258 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +138 -0
- package/dist/commands/remove.d.ts +23 -0
- package/dist/commands/remove.js +235 -0
- package/dist/commands/status.d.ts +41 -0
- package/dist/commands/status.js +147 -0
- package/dist/commands/up.d.ts +49 -0
- package/dist/commands/up.js +362 -0
- package/dist/commands/watch.d.ts +35 -0
- package/dist/commands/watch.js +324 -0
- package/dist/commands/worktree.d.ts +12 -0
- package/dist/commands/worktree.js +388 -0
- package/dist/end/condition.d.ts +28 -0
- package/dist/end/condition.js +77 -0
- package/dist/file/current.d.ts +13 -0
- package/dist/file/current.js +53 -0
- package/dist/file/lines.d.ts +25 -0
- package/dist/file/lines.js +277 -0
- package/dist/file/load.d.ts +7 -0
- package/dist/file/load.js +90 -0
- package/dist/file/paths.d.ts +6 -0
- package/dist/file/paths.js +77 -0
- package/dist/file/secrets.d.ts +6 -0
- package/dist/file/secrets.js +54 -0
- package/dist/file/signature.d.ts +13 -0
- package/dist/file/signature.js +36 -0
- package/dist/file/types.d.ts +130 -0
- package/dist/file/types.js +1 -0
- package/dist/file/validate.d.ts +13 -0
- package/dist/file/validate.js +702 -0
- package/dist/file/write.d.ts +11 -0
- package/dist/file/write.js +13 -0
- package/dist/herdr.d.ts +38 -0
- package/dist/herdr.js +227 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +6 -0
- package/dist/io.d.ts +10 -0
- package/dist/io.js +1 -0
- package/dist/launch/deliver.d.ts +10 -0
- package/dist/launch/deliver.js +39 -0
- package/dist/launch/execute.d.ts +46 -0
- package/dist/launch/execute.js +284 -0
- package/dist/launch/plan.d.ts +185 -0
- package/dist/launch/plan.js +280 -0
- package/dist/launch/rules.d.ts +25 -0
- package/dist/launch/rules.js +36 -0
- package/dist/log.d.ts +1 -0
- package/dist/log.js +24 -0
- package/dist/profiles/antigravity.yaml +66 -0
- package/dist/profiles/claude-code.yaml +69 -0
- package/dist/profiles/codex.yaml +77 -0
- package/dist/profiles/cursor.yaml +68 -0
- package/dist/profiles/index.d.ts +1 -0
- package/dist/profiles/index.js +1 -0
- package/dist/profiles/profile.d.ts +60 -0
- package/dist/profiles/profile.js +313 -0
- package/dist/profiles/quota.d.ts +27 -0
- package/dist/profiles/quota.js +57 -0
- package/dist/state.d.ts +61 -0
- package/dist/state.js +127 -0
- package/dist/status/compare.d.ts +26 -0
- package/dist/status/compare.js +110 -0
- package/dist/status/statusline.d.ts +9 -0
- package/dist/status/statusline.js +26 -0
- package/dist/store/store.d.ts +62 -0
- package/dist/store/store.js +116 -0
- package/dist/watch/check.d.ts +82 -0
- package/dist/watch/check.js +29 -0
- package/dist/watch/checks/approval.d.ts +2 -0
- package/dist/watch/checks/approval.js +16 -0
- package/dist/watch/checks/attention.d.ts +2 -0
- package/dist/watch/checks/attention.js +23 -0
- package/dist/watch/checks/budget.d.ts +2 -0
- package/dist/watch/checks/budget.js +141 -0
- package/dist/watch/checks/disk.d.ts +2 -0
- package/dist/watch/checks/disk.js +13 -0
- package/dist/watch/checks/extra.d.ts +2 -0
- package/dist/watch/checks/extra.js +18 -0
- package/dist/watch/checks/idle.d.ts +2 -0
- package/dist/watch/checks/idle.js +32 -0
- package/dist/watch/checks/load.d.ts +2 -0
- package/dist/watch/checks/load.js +11 -0
- package/dist/watch/checks/memory.d.ts +2 -0
- package/dist/watch/checks/memory.js +11 -0
- package/dist/watch/checks/missing.d.ts +2 -0
- package/dist/watch/checks/missing.js +12 -0
- package/dist/watch/checks/model-drift.d.ts +2 -0
- package/dist/watch/checks/model-drift.js +14 -0
- package/dist/watch/checks/swap-free.d.ts +2 -0
- package/dist/watch/checks/swap-free.js +12 -0
- package/dist/watch/checks/swap-growth.d.ts +2 -0
- package/dist/watch/checks/swap-growth.js +18 -0
- package/dist/watch/checks/team-idle.d.ts +2 -0
- package/dist/watch/checks/team-idle.js +18 -0
- package/dist/watch/checks/unsent.d.ts +2 -0
- package/dist/watch/checks/unsent.js +18 -0
- package/dist/watch/close.d.ts +19 -0
- package/dist/watch/close.js +40 -0
- package/dist/watch/dialect.d.ts +5 -0
- package/dist/watch/dialect.js +430 -0
- package/dist/watch/end.d.ts +11 -0
- package/dist/watch/end.js +17 -0
- package/dist/watch/machine.d.ts +38 -0
- package/dist/watch/machine.js +137 -0
- package/dist/watch/notify.d.ts +1 -0
- package/dist/watch/notify.js +15 -0
- package/dist/watch/pass.d.ts +42 -0
- package/dist/watch/pass.js +242 -0
- package/dist/watch/screen-core.d.ts +20 -0
- package/dist/watch/screen-core.js +356 -0
- package/dist/watch/screen-data.d.ts +65 -0
- package/dist/watch/screen-data.js +1 -0
- package/dist/watch/screen-file.d.ts +2 -0
- package/dist/watch/screen-file.js +269 -0
- package/dist/watch/screen.d.ts +24 -0
- package/dist/watch/screen.js +47 -0
- package/dist/worktree/place.d.ts +18 -0
- package/dist/worktree/place.js +126 -0
- package/dist/yaml.d.ts +30 -0
- package/dist/yaml.js +380 -0
- package/examples/checks/codex-quota +217 -0
- package/examples/team.yaml +76 -0
- package/package.json +29 -10
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { renderings, shape } from "./signature.js";
|
|
2
|
+
/** The text's lines, trailing spaces trimmed. */
|
|
3
|
+
export function splitLines(text) {
|
|
4
|
+
return text.split(/\r?\n/).map((line) => line.trimEnd());
|
|
5
|
+
}
|
|
6
|
+
/** Every line that matches a forbidden pattern. */
|
|
7
|
+
export function findForbidden(lines, patterns) {
|
|
8
|
+
const findings = [];
|
|
9
|
+
lines.forEach((text, index) => {
|
|
10
|
+
for (const pattern of patterns) {
|
|
11
|
+
if (pattern.regex.test(text)) {
|
|
12
|
+
findings.push({
|
|
13
|
+
kind: 'forbidden',
|
|
14
|
+
message: `forbidden pattern ${pattern.source}`,
|
|
15
|
+
line: index + 1,
|
|
16
|
+
text,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
return findings;
|
|
22
|
+
}
|
|
23
|
+
/** The indexes of the last run of non-empty lines: [start, end). */
|
|
24
|
+
function lastParagraph(lines) {
|
|
25
|
+
let end = lines.length;
|
|
26
|
+
while (end > 0 && lines[end - 1] === '')
|
|
27
|
+
end--;
|
|
28
|
+
let start = end;
|
|
29
|
+
while (start > 0 && lines[start - 1] !== '')
|
|
30
|
+
start--;
|
|
31
|
+
return [start, end];
|
|
32
|
+
}
|
|
33
|
+
const TRAILER_LINE = /^[A-Za-z0-9-]+\s*:\s*\S/;
|
|
34
|
+
const WHERE = {
|
|
35
|
+
'last-line': { commit: 'the last line', pr: 'the last line' },
|
|
36
|
+
trailer: { commit: 'the final trailer block', pr: 'the last paragraph' },
|
|
37
|
+
anywhere: { commit: 'a line of its own', pr: 'a line of its own' },
|
|
38
|
+
};
|
|
39
|
+
/** Empty when the text carries a signature of the team's at the position the rule says. */
|
|
40
|
+
export function checkSignature(input) {
|
|
41
|
+
const { lines, rule, ledger, trailers } = input;
|
|
42
|
+
const accepted = renderings(rule.template, ledger);
|
|
43
|
+
const [start, end] = lastParagraph(lines);
|
|
44
|
+
const where = WHERE[rule.position][trailers ? 'commit' : 'pr'];
|
|
45
|
+
let candidates;
|
|
46
|
+
if (rule.position === 'last-line')
|
|
47
|
+
candidates = lines.slice(Math.max(end - 1, 0), end);
|
|
48
|
+
else if (rule.position === 'anywhere')
|
|
49
|
+
candidates = lines;
|
|
50
|
+
else
|
|
51
|
+
candidates = trailers ? trailers.map((line) => line.trimEnd()) : lines.slice(start, end);
|
|
52
|
+
if (candidates.some((line) => accepted.has(line)))
|
|
53
|
+
return [];
|
|
54
|
+
const signed = lines.findIndex((line) => accepted.has(line));
|
|
55
|
+
if (signed !== -1) {
|
|
56
|
+
const text = lines[signed];
|
|
57
|
+
const inLastParagraph = signed >= start && signed < end;
|
|
58
|
+
if (trailers && rule.position === 'trailer' && inLastParagraph) {
|
|
59
|
+
const prose = lines.slice(start, end).filter((line) => !TRAILER_LINE.test(line));
|
|
60
|
+
if (prose.length > 0) {
|
|
61
|
+
return [
|
|
62
|
+
{
|
|
63
|
+
kind: 'signature-in-prose',
|
|
64
|
+
message: `the signature shares its paragraph with prose (${prose.map((line) => JSON.stringify(line)).join(', ')}), ` +
|
|
65
|
+
'so git reads no trailer block: put it in a paragraph of its own, with trailers only',
|
|
66
|
+
line: signed + 1,
|
|
67
|
+
text,
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return [
|
|
73
|
+
{
|
|
74
|
+
kind: 'signature-misplaced',
|
|
75
|
+
message: `the signature is not in ${where}`,
|
|
76
|
+
line: signed + 1,
|
|
77
|
+
text,
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
const form = shape(rule.template);
|
|
82
|
+
const unknown = [];
|
|
83
|
+
lines.forEach((text, index) => {
|
|
84
|
+
if (form.test(text)) {
|
|
85
|
+
unknown.push({
|
|
86
|
+
kind: 'signature-unknown',
|
|
87
|
+
message: 'a signature no seat of this team has had (the model, the version and the role must all match one seat)',
|
|
88
|
+
line: index + 1,
|
|
89
|
+
text,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
if (unknown.length > 0)
|
|
94
|
+
return unknown;
|
|
95
|
+
return [
|
|
96
|
+
{
|
|
97
|
+
kind: 'signature-missing',
|
|
98
|
+
message: `no signature: expected ${JSON.stringify(rule.template)} in ${where}`,
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type CheckConfig } from './config.ts';
|
|
2
|
+
import { type Finding } from './message.ts';
|
|
3
|
+
export interface CheckOptions {
|
|
4
|
+
/** A folder of the repository to read. */
|
|
5
|
+
cwd: string;
|
|
6
|
+
/** A range when it holds `..`, else one commit. */
|
|
7
|
+
ref: string;
|
|
8
|
+
/** Overrides the file's `identity.since` for this run. */
|
|
9
|
+
since?: string;
|
|
10
|
+
/** A pull request's body, to check as well. */
|
|
11
|
+
pullRequestBody?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface CommitReport {
|
|
14
|
+
hash: string;
|
|
15
|
+
subject: string;
|
|
16
|
+
/** Why the commit needs no signature; its forbidden patterns are still checked. */
|
|
17
|
+
exempt?: 'merge' | 'human';
|
|
18
|
+
findings: Finding[];
|
|
19
|
+
}
|
|
20
|
+
export interface CheckReport {
|
|
21
|
+
commits: CommitReport[];
|
|
22
|
+
skipped: number;
|
|
23
|
+
since?: string;
|
|
24
|
+
/** Present when a PR body was checked. */
|
|
25
|
+
pullRequest?: Finding[];
|
|
26
|
+
ok: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Checks the commits of `ref`, and a PR body when one is given. Read only. */
|
|
29
|
+
export declare function runCheck(config: CheckConfig, options: CheckOptions): CheckReport;
|
|
30
|
+
/** The report as `check` prints it: each offending commit and line, then a summary. */
|
|
31
|
+
export declare function formatReport(report: CheckReport): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { forbiddenPatterns } from "./config.js";
|
|
2
|
+
import { selectCommits } from "./git.js";
|
|
3
|
+
import { checkSignature, findForbidden, splitLines } from "./message.js";
|
|
4
|
+
function exemption(commit, config, humans) {
|
|
5
|
+
if (config.commits.exemptMerge && commit.parents.length > 1)
|
|
6
|
+
return 'merge';
|
|
7
|
+
if (humans.has(commit.authorEmail.toLowerCase()))
|
|
8
|
+
return 'human';
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
/** Checks the commits of `ref`, and a PR body when one is given. Read only. */
|
|
12
|
+
export function runCheck(config, options) {
|
|
13
|
+
const patterns = forbiddenPatterns(config);
|
|
14
|
+
const humans = new Set(config.humans.map((email) => email.toLowerCase()));
|
|
15
|
+
const selection = selectCommits(options.cwd, options.ref, options.since ?? config.since);
|
|
16
|
+
const commits = selection.commits.map((commit) => {
|
|
17
|
+
const lines = splitLines(commit.message);
|
|
18
|
+
const exempt = exemption(commit, config, humans);
|
|
19
|
+
const findings = findForbidden(lines, patterns);
|
|
20
|
+
if (!exempt) {
|
|
21
|
+
findings.push(...checkSignature({ lines, rule: config.commits, ledger: config.ledger, trailers: commit.trailers }));
|
|
22
|
+
}
|
|
23
|
+
return { hash: commit.hash, subject: commit.subject, exempt, findings };
|
|
24
|
+
});
|
|
25
|
+
let pullRequest;
|
|
26
|
+
if (options.pullRequestBody !== undefined) {
|
|
27
|
+
const lines = splitLines(options.pullRequestBody);
|
|
28
|
+
pullRequest = [
|
|
29
|
+
...findForbidden(lines, patterns),
|
|
30
|
+
...checkSignature({ lines, rule: config.pullRequests, ledger: config.ledger }),
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
const ok = commits.every((commit) => commit.findings.length === 0) && (pullRequest?.length ?? 0) === 0;
|
|
34
|
+
return { commits, skipped: selection.skipped, since: selection.since, pullRequest, ok };
|
|
35
|
+
}
|
|
36
|
+
function plural(count, noun) {
|
|
37
|
+
return `${count} ${noun}${count === 1 ? '' : 's'}`;
|
|
38
|
+
}
|
|
39
|
+
function findingLines(finding) {
|
|
40
|
+
const where = finding.line === undefined ? '' : `line ${finding.line}: `;
|
|
41
|
+
const lines = [` ${where}${finding.message}`];
|
|
42
|
+
if (finding.text !== undefined)
|
|
43
|
+
lines.push(` ${finding.text}`);
|
|
44
|
+
return lines;
|
|
45
|
+
}
|
|
46
|
+
/** The report as `check` prints it: each offending commit and line, then a summary. */
|
|
47
|
+
export function formatReport(report) {
|
|
48
|
+
const out = [];
|
|
49
|
+
for (const commit of report.commits) {
|
|
50
|
+
if (commit.findings.length === 0)
|
|
51
|
+
continue;
|
|
52
|
+
out.push(`${commit.hash.slice(0, 10)} ${commit.subject}`);
|
|
53
|
+
for (const finding of commit.findings)
|
|
54
|
+
out.push(...findingLines(finding));
|
|
55
|
+
}
|
|
56
|
+
if (report.pullRequest && report.pullRequest.length > 0) {
|
|
57
|
+
out.push('pull request body');
|
|
58
|
+
for (const finding of report.pullRequest)
|
|
59
|
+
out.push(...findingLines(finding));
|
|
60
|
+
}
|
|
61
|
+
const failed = report.commits.filter((commit) => commit.findings.length > 0).length;
|
|
62
|
+
const exempt = report.commits.filter((commit) => commit.exempt).length;
|
|
63
|
+
const parts = [`${plural(report.commits.length, 'commit')} checked`];
|
|
64
|
+
if (exempt > 0)
|
|
65
|
+
parts.push(`${exempt} by a human or a merge`);
|
|
66
|
+
if (report.skipped > 0)
|
|
67
|
+
parts.push(`${report.skipped} skipped (since ${report.since?.slice(0, 10)})`);
|
|
68
|
+
if (report.pullRequest)
|
|
69
|
+
parts.push('1 pull request body checked');
|
|
70
|
+
const problems = [];
|
|
71
|
+
if (failed > 0)
|
|
72
|
+
problems.push(`${plural(failed, 'commit')} refused`);
|
|
73
|
+
if (report.pullRequest && report.pullRequest.length > 0)
|
|
74
|
+
problems.push('the pull request body refused');
|
|
75
|
+
out.push(`team check: ${parts.join(', ')}: ${report.ok ? 'ok' : problems.join(', ')}`);
|
|
76
|
+
return `${out.join('\n')}\n`;
|
|
77
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SeatIdentity } from './config.ts';
|
|
2
|
+
/** The signature line a seat writes, from the project's template. */
|
|
3
|
+
export declare function render(template: string, seat: SeatIdentity): string;
|
|
4
|
+
/** Every accepted signature line. */
|
|
5
|
+
export declare function renderings(template: string, ledger: readonly SeatIdentity[]): Set<string>;
|
|
6
|
+
/**
|
|
7
|
+
* Matches a line that has the template's fixed words around any values: a
|
|
8
|
+
* signature by its form, whether or not a seat ever had those values.
|
|
9
|
+
*/
|
|
10
|
+
export declare function shape(template: string): RegExp;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { renderSignature } from "../file/signature.js";
|
|
2
|
+
const PLACEHOLDER = /\{(display|role|model|version)\}/g;
|
|
3
|
+
/** The signature line a seat writes, from the project's template. */
|
|
4
|
+
export function render(template, seat) {
|
|
5
|
+
return renderSignature(template, seat).trimEnd();
|
|
6
|
+
}
|
|
7
|
+
/** Every accepted signature line. */
|
|
8
|
+
export function renderings(template, ledger) {
|
|
9
|
+
return new Set(ledger.map((seat) => render(template, seat)));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Matches a line that has the template's fixed words around any values: a
|
|
13
|
+
* signature by its form, whether or not a seat ever had those values.
|
|
14
|
+
*/
|
|
15
|
+
export function shape(template) {
|
|
16
|
+
const literals = template
|
|
17
|
+
.trimEnd()
|
|
18
|
+
.split(PLACEHOLDER)
|
|
19
|
+
.filter((_, index) => index % 2 === 0);
|
|
20
|
+
const escaped = literals.map((literal) => literal.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
21
|
+
return new RegExp(`^${escaped.join('(.+?)')}$`);
|
|
22
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import type { Command, Io } from './io.ts';
|
|
3
|
+
export declare const commands: Record<string, () => Promise<{
|
|
4
|
+
default: Command;
|
|
5
|
+
USAGE: string;
|
|
6
|
+
}>>;
|
|
7
|
+
export declare function version(): string;
|
|
8
|
+
export declare function main(argv: string[], io: Io): Promise<number>;
|
|
9
|
+
export declare function processIo(): Io;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
// Each command is loaded only when it is called. A slice adds its line here. The table is exported
|
|
5
|
+
// so a test can walk it: every command, `--help`, `-h` and a usage line.
|
|
6
|
+
export const commands = {
|
|
7
|
+
approve: () => import("./commands/approve.js"),
|
|
8
|
+
check: () => import("./commands/check.js"),
|
|
9
|
+
doctor: () => import("./commands/doctor.js"),
|
|
10
|
+
down: () => import("./commands/down.js"),
|
|
11
|
+
init: () => import("./commands/init.js"),
|
|
12
|
+
status: () => import("./commands/status.js"),
|
|
13
|
+
up: () => import("./commands/up.js"),
|
|
14
|
+
watch: () => import("./commands/watch.js"),
|
|
15
|
+
worktree: () => import("./commands/worktree.js"),
|
|
16
|
+
add: () => import("./commands/add.js"),
|
|
17
|
+
remove: () => import("./commands/remove.js"),
|
|
18
|
+
};
|
|
19
|
+
const USAGE = `team: set up, change and watch a project's team of AI agents
|
|
20
|
+
|
|
21
|
+
Usage: team <command> [options]
|
|
22
|
+
|
|
23
|
+
Commands:
|
|
24
|
+
{commands}
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
--version print the version
|
|
28
|
+
--help print this text
|
|
29
|
+
|
|
30
|
+
The team is declared in <project>/.agents/team.yaml.
|
|
31
|
+
`;
|
|
32
|
+
export function version() {
|
|
33
|
+
const text = readFileSync(new URL('../package.json', import.meta.url), 'utf8');
|
|
34
|
+
return JSON.parse(text).version;
|
|
35
|
+
}
|
|
36
|
+
export async function main(argv, io) {
|
|
37
|
+
const [name, ...rest] = argv;
|
|
38
|
+
const names = Object.keys(commands);
|
|
39
|
+
const usage = USAGE.replace('{commands}', names.length ? names.map((n) => ` ${n}`).join('\n') : ' (none in this build)');
|
|
40
|
+
if (!name || name === '--help' || name === '-h' || name === 'help') {
|
|
41
|
+
io.stdout(usage);
|
|
42
|
+
return name ? 0 : 2;
|
|
43
|
+
}
|
|
44
|
+
if (name === '--version' || name === '-V') {
|
|
45
|
+
io.stdout(`${version()}\n`);
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
const load = commands[name];
|
|
49
|
+
if (!load) {
|
|
50
|
+
io.stderr(`team: unknown command "${name}"\n\n${usage}`);
|
|
51
|
+
return 2;
|
|
52
|
+
}
|
|
53
|
+
const command = await load();
|
|
54
|
+
// The one path for every command's `--help`/`-h`: it prints the command's usage and stops,
|
|
55
|
+
// before any option parsing, file reading, caller check or herdr call in the command itself.
|
|
56
|
+
if (rest.includes('--help') || rest.includes('-h')) {
|
|
57
|
+
io.stdout(command.USAGE);
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
return command.default(rest, io);
|
|
61
|
+
}
|
|
62
|
+
export function processIo() {
|
|
63
|
+
return {
|
|
64
|
+
stdout: (text) => void process.stdout.write(text),
|
|
65
|
+
stderr: (text) => void process.stderr.write(text),
|
|
66
|
+
cwd: process.cwd(),
|
|
67
|
+
env: process.env,
|
|
68
|
+
stdinIsTTY: Boolean(process.stdin.isTTY),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
72
|
+
main(process.argv.slice(2), processIo()).then((code) => { process.exitCode = code; }, (error) => {
|
|
73
|
+
process.stderr.write(`team: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
});
|
|
76
|
+
}
|
package/dist/clis.d.ts
ADDED
package/dist/clis.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const PROFILES = [
|
|
2
|
+
{ cli: 'claude-code', processes: ['claude'] },
|
|
3
|
+
{ cli: 'codex', processes: ['codex'] },
|
|
4
|
+
{ cli: 'cursor', processes: ['cursor-agent'] },
|
|
5
|
+
{ cli: 'grok', processes: ['grok'] },
|
|
6
|
+
{ cli: 'antigravity', processes: ['agy'] },
|
|
7
|
+
];
|
|
8
|
+
export const CLIS = PROFILES.map((profile) => profile.cli);
|
|
9
|
+
export const CLI_PROCESSES = PROFILES.flatMap((profile) => profile.processes);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type DoctorSources } from '../commands/doctor.ts';
|
|
2
|
+
import { type Launch } from '../commands/up.ts';
|
|
3
|
+
import { type HerdrAgent } from '../herdr.ts';
|
|
4
|
+
import type { Command, Io } from '../io.ts';
|
|
5
|
+
import { type Machine } from '../watch/machine.ts';
|
|
6
|
+
export type AddSources = {
|
|
7
|
+
home: string;
|
|
8
|
+
sessionState(session: string): 'absent' | 'running' | 'stopped' | null;
|
|
9
|
+
agents(session: string): HerdrAgent[] | null;
|
|
10
|
+
workspaces(session: string): {
|
|
11
|
+
id: string;
|
|
12
|
+
label: string;
|
|
13
|
+
}[] | null;
|
|
14
|
+
doctor: DoctorSources;
|
|
15
|
+
machine?: (root: string) => Machine;
|
|
16
|
+
now(): Date;
|
|
17
|
+
launch: Launch;
|
|
18
|
+
};
|
|
19
|
+
export declare const realSources: AddSources;
|
|
20
|
+
export declare const USAGE = "Usage: team add <name> [--dry-run] [--session <name>] [--file <path>]\n team add --temporary --like <seat> --until <result:path|merged:branch> [--worktree <task>] [--dry-run] [--session <name>] [--file <path>]\n";
|
|
21
|
+
export declare const add: Command;
|
|
22
|
+
export default add;
|
|
23
|
+
export declare function runAdd(argv: string[], io: Io, sources?: AddSources): Promise<number>;
|