@detergent-software/conductor 3.0.0-dev.3 → 3.0.0-dev.30
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/CHANGELOG.md +241 -0
- package/README.md +201 -63
- package/build/brief-cli.js +1 -1
- package/build/brief-cli.js.map +1 -1
- package/build/broker-cli.js +24 -25
- package/build/broker-cli.js.map +1 -1
- package/build/broker.js +66 -92
- package/build/broker.js.map +1 -1
- package/build/claude.js +50 -7
- package/build/claude.js.map +1 -1
- package/build/cli.js +143 -63
- package/build/cli.js.map +1 -1
- package/build/clones.js +92 -0
- package/build/clones.js.map +1 -0
- package/build/code-host-cli.js +116 -32
- package/build/code-host-cli.js.map +1 -1
- package/build/code-host-service.js +8 -0
- package/build/code-host-service.js.map +1 -1
- package/build/code-host.js +22 -11
- package/build/code-host.js.map +1 -1
- package/build/config.js +58 -14
- package/build/config.js.map +1 -1
- package/build/console.js +40 -24
- package/build/console.js.map +1 -1
- package/build/credentials.js +42 -29
- package/build/credentials.js.map +1 -1
- package/build/daemon.js +38 -22
- package/build/daemon.js.map +1 -1
- package/build/help.js +179 -42
- package/build/help.js.map +1 -1
- package/build/history.js +4 -2
- package/build/history.js.map +1 -1
- package/build/host/credential-manager.js +109 -0
- package/build/host/credential-manager.js.map +1 -0
- package/build/host/keychain.js +44 -0
- package/build/host/keychain.js.map +1 -0
- package/build/host/launchd.js +118 -0
- package/build/host/launchd.js.map +1 -0
- package/build/host/libsecret.js +40 -0
- package/build/host/libsecret.js.map +1 -0
- package/build/host/macos-terminal.js +63 -0
- package/build/host/macos-terminal.js.map +1 -0
- package/build/host/platform.js +4 -0
- package/build/host/platform.js.map +1 -0
- package/build/host/secrets.js +46 -0
- package/build/host/secrets.js.map +1 -0
- package/build/host/shell.js +26 -0
- package/build/host/shell.js.map +1 -0
- package/build/host/startup.js +26 -0
- package/build/host/startup.js.map +1 -0
- package/build/host/systemd.js +91 -0
- package/build/host/systemd.js.map +1 -0
- package/build/host/task-scheduler.js +78 -0
- package/build/host/task-scheduler.js.map +1 -0
- package/build/host/terminal.js +46 -0
- package/build/host/terminal.js.map +1 -0
- package/build/host/tmux.js +39 -0
- package/build/host/tmux.js.map +1 -0
- package/build/host/windows-terminal.js +46 -0
- package/build/host/windows-terminal.js.map +1 -0
- package/build/in-process.js +6 -5
- package/build/in-process.js.map +1 -1
- package/build/inbox.js +63 -17
- package/build/inbox.js.map +1 -1
- package/build/init.js +508 -0
- package/build/init.js.map +1 -0
- package/build/landing.js +86 -7
- package/build/landing.js.map +1 -1
- package/build/lib/providers/ado/code-host.js +5 -1
- package/build/lib/providers/ado/code-host.js.map +1 -1
- package/build/lib/providers/ado/pipelines.js +130 -22
- package/build/lib/providers/ado/pipelines.js.map +1 -1
- package/build/lib/providers/ado/trigger.js +5 -0
- package/build/lib/providers/ado/trigger.js.map +1 -1
- package/build/lib/providers/ado/writes.js +10 -1
- package/build/lib/providers/ado/writes.js.map +1 -1
- package/build/lib/providers/github/actions.js +90 -3
- package/build/lib/providers/github/actions.js.map +1 -1
- package/build/lib/providers/github/code-host.js +2 -0
- package/build/lib/providers/github/code-host.js.map +1 -1
- package/build/lib/providers/github/writes.js +24 -2
- package/build/lib/providers/github/writes.js.map +1 -1
- package/build/lib/providers/index.js +4 -3
- package/build/lib/providers/index.js.map +1 -1
- package/build/lib/providers/npm/stage.js +28 -5
- package/build/lib/providers/npm/stage.js.map +1 -1
- package/build/lib/providers/timing.js +10 -0
- package/build/lib/providers/timing.js.map +1 -0
- package/build/lib/providers/verdicts/pr.js +47 -6
- package/build/lib/providers/verdicts/pr.js.map +1 -1
- package/build/lib/providers/verdicts/runs.js +12 -2
- package/build/lib/providers/verdicts/runs.js.map +1 -1
- package/build/local-files.js +95 -0
- package/build/local-files.js.map +1 -0
- package/build/manager.js +171 -47
- package/build/manager.js.map +1 -1
- package/build/note.js +1 -1
- package/build/note.js.map +1 -1
- package/build/notify.js +101 -39
- package/build/notify.js.map +1 -1
- package/build/npm-reads.js +323 -0
- package/build/npm-reads.js.map +1 -0
- package/build/paths.js +44 -1
- package/build/paths.js.map +1 -1
- package/build/plugin.js +10 -0
- package/build/plugin.js.map +1 -1
- package/build/profile-fields.js +149 -0
- package/build/profile-fields.js.map +1 -0
- package/build/profile-layers.js +264 -0
- package/build/profile-layers.js.map +1 -0
- package/build/profile.js +70 -44
- package/build/profile.js.map +1 -1
- package/build/project-check-cli.js +18 -10
- package/build/project-check-cli.js.map +1 -1
- package/build/project-check.js +187 -98
- package/build/project-check.js.map +1 -1
- package/build/pushover.js +5 -5
- package/build/pushover.js.map +1 -1
- package/build/registry.js +195 -36
- package/build/registry.js.map +1 -1
- package/build/release-kinds.js +34 -0
- package/build/release-kinds.js.map +1 -0
- package/build/rule-actions.js +4 -2
- package/build/rule-actions.js.map +1 -1
- package/build/rules-service.js +101 -44
- package/build/rules-service.js.map +1 -1
- package/build/rules.js +15 -15
- package/build/rules.js.map +1 -1
- package/build/run.js +20 -7
- package/build/run.js.map +1 -1
- package/build/runs.js +200 -45
- package/build/runs.js.map +1 -1
- package/build/scheduler.js +11 -1
- package/build/scheduler.js.map +1 -1
- package/build/server.js +6 -25
- package/build/server.js.map +1 -1
- package/build/spend.js +4 -0
- package/build/spend.js.map +1 -1
- package/build/store.js +82 -25
- package/build/store.js.map +1 -1
- package/build/timeline.js +104 -0
- package/build/timeline.js.map +1 -0
- package/build/tracker-reads-cli.js +4 -5
- package/build/tracker-reads-cli.js.map +1 -1
- package/build/train-cli.js +6 -5
- package/build/train-cli.js.map +1 -1
- package/build/upgrade.js +8 -9
- package/build/upgrade.js.map +1 -1
- package/build/wait.js +6 -6
- package/build/wait.js.map +1 -1
- package/build/watchers/ado.js +11 -3
- package/build/watchers/ado.js.map +1 -1
- package/build/watchers/github.js +15 -4
- package/build/watchers/github.js.map +1 -1
- package/build/watchers/npm.js +6 -4
- package/build/watchers/npm.js.map +1 -1
- package/build/webhook.js +80 -0
- package/build/webhook.js.map +1 -0
- package/build/worktrees.js +199 -92
- package/build/worktrees.js.map +1 -1
- package/package.json +61 -61
- package/plugin/skills/conductor-console/SKILL.md +4 -4
- package/plugin/skills/work-item/SKILL.md +13 -11
- package/build/checkout-sync.js +0 -249
- package/build/checkout-sync.js.map +0 -1
- package/build/credential.js +0 -66
- package/build/credential.js.map +0 -1
- package/build/startup.js +0 -92
- package/build/startup.js.map +0 -1
- package/build/tab.js +0 -32
- package/build/tab.js.map +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { gt, lt, valid } from 'semver';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { parseJsonc } from "./jsonc.js";
|
|
4
|
+
// The first conductor that reads protocol 2: one older refuses the profile on `protocol` first, so
|
|
5
|
+
// a field it already read needs no minimum.
|
|
6
|
+
export const PROTOCOL_2 = '2.0.0';
|
|
7
|
+
// The conductor that first read each profile field as it is written today (ATK-577): a reshaped
|
|
8
|
+
// field takes the version of its new shape. A profile that writes a field newer than PROTOCOL_2
|
|
9
|
+
// needs its `conductor` minimum at or above that version. The type makes a new field a typecheck
|
|
10
|
+
// error until it has a version; the README (On PATH) says which one a new field takes. A key
|
|
11
|
+
// two tracker kinds share takes the newer kind's (Linear's releaseWait is 2.0.0's).
|
|
12
|
+
export const FIELD_SINCE = {
|
|
13
|
+
approvers: PROTOCOL_2,
|
|
14
|
+
areaRelease: '2.2.0-dev.2',
|
|
15
|
+
baseBranch: PROTOCOL_2,
|
|
16
|
+
ci: PROTOCOL_2,
|
|
17
|
+
'ci.kind': PROTOCOL_2,
|
|
18
|
+
codeHost: PROTOCOL_2,
|
|
19
|
+
'codeHost.kind': PROTOCOL_2,
|
|
20
|
+
'codeHost.organization': PROTOCOL_2,
|
|
21
|
+
'codeHost.owner': PROTOCOL_2,
|
|
22
|
+
'codeHost.project': PROTOCOL_2,
|
|
23
|
+
'codeHost.repo': PROTOCOL_2,
|
|
24
|
+
'codeHost.repository': PROTOCOL_2,
|
|
25
|
+
conductor: '3.0.0-dev.9',
|
|
26
|
+
gates: PROTOCOL_2,
|
|
27
|
+
health: PROTOCOL_2,
|
|
28
|
+
localFilesFrom: '3.0.0-dev.4',
|
|
29
|
+
name: PROTOCOL_2,
|
|
30
|
+
npm: PROTOCOL_2,
|
|
31
|
+
'npm.package': PROTOCOL_2,
|
|
32
|
+
ports: '3.0.0-dev.4',
|
|
33
|
+
protocol: PROTOCOL_2,
|
|
34
|
+
release: PROTOCOL_2,
|
|
35
|
+
'release.handoff': PROTOCOL_2,
|
|
36
|
+
releases: PROTOCOL_2,
|
|
37
|
+
'releases.key': PROTOCOL_2,
|
|
38
|
+
'releases.kind': PROTOCOL_2,
|
|
39
|
+
'releases.package': PROTOCOL_2,
|
|
40
|
+
'releases.pipeline': PROTOCOL_2,
|
|
41
|
+
rules: PROTOCOL_2,
|
|
42
|
+
'rules.do': PROTOCOL_2,
|
|
43
|
+
'rules.id': PROTOCOL_2,
|
|
44
|
+
'rules.note': PROTOCOL_2,
|
|
45
|
+
'rules.when': PROTOCOL_2,
|
|
46
|
+
setup: '3.0.0-dev.4',
|
|
47
|
+
skills: PROTOCOL_2,
|
|
48
|
+
'skills.aliases': PROTOCOL_2,
|
|
49
|
+
'skills.verify': PROTOCOL_2,
|
|
50
|
+
'skills.work': PROTOCOL_2,
|
|
51
|
+
slots: PROTOCOL_2,
|
|
52
|
+
tracker: PROTOCOL_2,
|
|
53
|
+
'tracker.areaLabelGroup': PROTOCOL_2,
|
|
54
|
+
'tracker.areaPrefix': PROTOCOL_2,
|
|
55
|
+
'tracker.areaRoot': PROTOCOL_2,
|
|
56
|
+
'tracker.humanLabel': PROTOCOL_2,
|
|
57
|
+
'tracker.key': PROTOCOL_2,
|
|
58
|
+
'tracker.kind': PROTOCOL_2,
|
|
59
|
+
'tracker.organization': PROTOCOL_2,
|
|
60
|
+
'tracker.owner': PROTOCOL_2,
|
|
61
|
+
'tracker.priority': PROTOCOL_2,
|
|
62
|
+
'tracker.project': PROTOCOL_2,
|
|
63
|
+
'tracker.query': PROTOCOL_2,
|
|
64
|
+
'tracker.queue': PROTOCOL_2,
|
|
65
|
+
'tracker.queueLabel': PROTOCOL_2,
|
|
66
|
+
'tracker.releaseWait': '2.2.0-dev.3',
|
|
67
|
+
'tracker.releaseWait.label': '2.2.0-dev.3',
|
|
68
|
+
'tracker.releaseWait.shipped': '2.2.0-dev.3',
|
|
69
|
+
'tracker.repo': PROTOCOL_2,
|
|
70
|
+
'tracker.states': PROTOCOL_2,
|
|
71
|
+
'tracker.team': PROTOCOL_2,
|
|
72
|
+
'tracker.transitions': PROTOCOL_2,
|
|
73
|
+
'tracker.transitions.landed': PROTOCOL_2,
|
|
74
|
+
'tracker.transitions.pr-opened': PROTOCOL_2,
|
|
75
|
+
'tracker.transitions.started': PROTOCOL_2,
|
|
76
|
+
'tracker.triageLabel': PROTOCOL_2,
|
|
77
|
+
'tracker.workItemType': PROTOCOL_2,
|
|
78
|
+
train: PROTOCOL_2,
|
|
79
|
+
'train.maxHours': PROTOCOL_2,
|
|
80
|
+
'train.minimum': PROTOCOL_2,
|
|
81
|
+
'train.repo': PROTOCOL_2,
|
|
82
|
+
weight: PROTOCOL_2,
|
|
83
|
+
work: PROTOCOL_2,
|
|
84
|
+
'work.hooks': PROTOCOL_2,
|
|
85
|
+
'work.landing': PROTOCOL_2,
|
|
86
|
+
'work.landing.review': PROTOCOL_2,
|
|
87
|
+
'work.landing.reviewTiers': PROTOCOL_2,
|
|
88
|
+
workers: PROTOCOL_2,
|
|
89
|
+
'workers.tab': PROTOCOL_2,
|
|
90
|
+
};
|
|
91
|
+
// minimumProblems with the version that clears them, for `conduct project check`'s fix (ATK-580).
|
|
92
|
+
export function minimumCheck(text, source, since = FIELD_SINCE) {
|
|
93
|
+
const profile = parseJsonc(text, z.unknown(), source);
|
|
94
|
+
const conductor = typeof profile === 'object' && profile !== null && 'conductor' in profile ? profile.conductor : undefined;
|
|
95
|
+
const minimum = typeof conductor === 'string' ? valid(conductor) : null;
|
|
96
|
+
const theirs = minimum !== null
|
|
97
|
+
? `the profile's minimum is ${minimum}`
|
|
98
|
+
: conductor === undefined
|
|
99
|
+
? 'the profile sets no "conductor" minimum'
|
|
100
|
+
: `the profile's minimum ${JSON.stringify(conductor)} is not a version`;
|
|
101
|
+
// `tracker` and `tracker.releaseWait` for `tracker.releaseWait.label`.
|
|
102
|
+
const parents = new Set();
|
|
103
|
+
for (const path of Object.keys(since))
|
|
104
|
+
for (let dot = path.indexOf('.'); dot !== -1; dot = path.indexOf('.', dot + 1))
|
|
105
|
+
parents.add(path.slice(0, dot));
|
|
106
|
+
const problems = [];
|
|
107
|
+
let needs = null;
|
|
108
|
+
for (const path of new Set(writtenFields(profile, parents))) {
|
|
109
|
+
const version = since[path];
|
|
110
|
+
if (version === undefined)
|
|
111
|
+
problems.push(`${path}: no conductor version recorded for it in profile-fields.ts`);
|
|
112
|
+
else if (lt(minimum ?? PROTOCOL_2, version)) {
|
|
113
|
+
problems.push(`${path}: needs conductor ${version} or newer; ${theirs}`);
|
|
114
|
+
if (needs === null || gt(version, needs))
|
|
115
|
+
needs = version;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Raising the minimum writes `conductor`, so the version also covers the conductor that reads it.
|
|
119
|
+
const key = since.conductor;
|
|
120
|
+
if (needs !== null && key !== undefined && gt(key, needs))
|
|
121
|
+
needs = key;
|
|
122
|
+
return { needs, problems };
|
|
123
|
+
}
|
|
124
|
+
// Where a profile's `conductor` minimum is below a field it writes, one line each: a field newer
|
|
125
|
+
// than the minimum, or than PROTOCOL_2 when it sets none, and a field with no recorded version. A
|
|
126
|
+
// conductor test holds ATK's profile to it, so a PR that writes a new field raises the minimum too.
|
|
127
|
+
export function minimumProblems(text, source, since = FIELD_SINCE) {
|
|
128
|
+
return minimumCheck(text, source, since).problems;
|
|
129
|
+
}
|
|
130
|
+
// The fields a profile writes, by their paths in FIELD_SINCE, in the order it writes them. Fields
|
|
131
|
+
// a default fills in aren't written, so they need nothing. It looks inside a key only when it is
|
|
132
|
+
// one of `parents`, the paths FIELD_SINCE records fields below, so a record's own names (health's
|
|
133
|
+
// environments) aren't fields.
|
|
134
|
+
function* writtenFields(value, parents, prefix = '') {
|
|
135
|
+
if (Array.isArray(value)) {
|
|
136
|
+
for (const item of value)
|
|
137
|
+
yield* writtenFields(item, parents, prefix);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (value === null || typeof value !== 'object')
|
|
141
|
+
return;
|
|
142
|
+
for (const [key, child] of Object.entries(value)) {
|
|
143
|
+
const path = prefix === '' ? key : `${prefix}.${key}`;
|
|
144
|
+
yield path;
|
|
145
|
+
if (parents.has(path))
|
|
146
|
+
yield* writtenFields(child, parents, path);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=profile-fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-fields.js","sourceRoot":"","sources":["../src/profile-fields.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { lt, valid } from 'semver';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { ConductorError, isMissing } from "./errors.js";
|
|
7
|
+
import { issueLines, parseJsonc } from "./jsonc.js";
|
|
8
|
+
import { packageInfo } from "./package-info.js";
|
|
9
|
+
import { PROJECT_NAME, projectOfPath, projectPaths } from "./paths.js";
|
|
10
|
+
import { PROFILE_FILE, profileSchema, ProfileTooNewError } from "./profile.js";
|
|
11
|
+
import { PROTOCOL } from "./protocol.js";
|
|
12
|
+
// A project's profile in layers (design note v2, section 7): the machine's
|
|
13
|
+
// <home>/projects/<name>.json over the repo's optional .claude/conductor.json. Objects merge key by
|
|
14
|
+
// key with the machine winning, arrays are replaced whole, and `rules` stack: the machine's first,
|
|
15
|
+
// so one of its rules shadows a repo rule with the same id. The merge runs on the files as written,
|
|
16
|
+
// before the schema reads them, so a machine file beside a repo profile can be partial.
|
|
17
|
+
export const LAYERS = ['machine', 'repo'];
|
|
18
|
+
export const profileSourcesSchema = z.object({
|
|
19
|
+
// Each field's source by its dotted path, an array counting as one value; `default` when neither
|
|
20
|
+
// file writes it. `rules` is listed rule by rule instead.
|
|
21
|
+
fields: z.record(z.string(), z.enum(['default', ...LAYERS])),
|
|
22
|
+
// The machine profile, when there is one.
|
|
23
|
+
machine: z.string().nullable(),
|
|
24
|
+
// Where the repo layer was read, when there is one: `.claude/conductor.json at origin/<base>`.
|
|
25
|
+
repo: z.string().nullable(),
|
|
26
|
+
// The layer of each rule of the profile's `rules`, in order.
|
|
27
|
+
rules: z.array(z.enum(LAYERS)),
|
|
28
|
+
});
|
|
29
|
+
export const gitSync = (args, cwd) => {
|
|
30
|
+
const result = spawnSync('git', args, { cwd, encoding: 'utf8', timeout: 30_000, windowsHide: true });
|
|
31
|
+
return {
|
|
32
|
+
code: result.status ?? 1,
|
|
33
|
+
stderr: result.stderr ?? (result.error ? String(result.error) : ''),
|
|
34
|
+
stdout: result.stdout ?? '',
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
// The repo layer's path inside a commit: git spells it with forward slashes on every platform.
|
|
38
|
+
const PROFILE_IN_GIT = '.claude/conductor.json';
|
|
39
|
+
const rawSchema = z.record(z.string(), z.unknown());
|
|
40
|
+
// The profile the worker verbs read where they run, without the daemon: the checkout's own
|
|
41
|
+
// .claude/conductor.json (the branch's, as a worker's change to it would be) under the project's
|
|
42
|
+
// machine file. The project is the one whose folder the checkout is in (<home>/projects/<name>/…),
|
|
43
|
+
// else the one its own profile names, so a person's worktree of their own checkout reads too.
|
|
44
|
+
// Null when neither layer exists: no project's checkout.
|
|
45
|
+
export function checkoutProfile(paths, checkout) {
|
|
46
|
+
const repo = readLayer(join(checkout, PROFILE_FILE));
|
|
47
|
+
const name = projectOfPath(paths, checkout) ?? (typeof repo?.raw.name === 'string' ? repo.raw.name : null);
|
|
48
|
+
// A name the schema will refuse names no machine file either.
|
|
49
|
+
const machine = name !== null && PROJECT_NAME.test(name) ? readLayer(projectPaths(paths, name).profile) : null;
|
|
50
|
+
if (name === null || (machine === null && repo === null))
|
|
51
|
+
return null;
|
|
52
|
+
return layerProfile(name, { machine, repo });
|
|
53
|
+
}
|
|
54
|
+
// The layers merged and read: each layer's `conductor` minimum first, then the protocol, then the
|
|
55
|
+
// schema, so a profile written for another version gets one clear message. `name` is the project's
|
|
56
|
+
// registered name, which a machine file may repeat but not change.
|
|
57
|
+
export function layerProfile(name, layers, running) {
|
|
58
|
+
const { machine, repo } = layers;
|
|
59
|
+
const present = [machine, repo].filter((l) => l !== null);
|
|
60
|
+
const source = present.map((l) => l.label).join(' over ');
|
|
61
|
+
if (machine !== null && machine.raw.name !== undefined && machine.raw.name !== name) {
|
|
62
|
+
throw new ConductorError(`${machine.label} renames the project`, [
|
|
63
|
+
`name: ${JSON.stringify(machine.raw.name)} is not "${name}", the name its file and folder give it; drop it, or put "${name}" back`,
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
for (const layer of present) {
|
|
67
|
+
const minimum = typeof layer.raw.conductor === 'string' ? valid(layer.raw.conductor) : null;
|
|
68
|
+
if (minimum === null)
|
|
69
|
+
continue;
|
|
70
|
+
const version = running ?? packageInfo().version;
|
|
71
|
+
if (lt(version, minimum))
|
|
72
|
+
throw new ProfileTooNewError(layer.label, minimum, version);
|
|
73
|
+
}
|
|
74
|
+
const merged = mergeLayers(machine?.raw ?? {}, repo?.raw ?? {});
|
|
75
|
+
merged.name = name;
|
|
76
|
+
if (merged.protocol !== PROTOCOL) {
|
|
77
|
+
throw new ConductorError(`${source} is invalid`, [
|
|
78
|
+
`protocol: ${JSON.stringify(merged.protocol ?? null)} is not supported; this conductor speaks protocol ${PROTOCOL}`,
|
|
79
|
+
]);
|
|
80
|
+
}
|
|
81
|
+
const parsed = profileSchema.safeParse(merged);
|
|
82
|
+
if (!parsed.success)
|
|
83
|
+
throw new ConductorError(`${source} is invalid`, issueLines(parsed.error));
|
|
84
|
+
const machineRules = Array.isArray(machine?.raw.rules) ? machine.raw.rules.length : 0;
|
|
85
|
+
return {
|
|
86
|
+
layers,
|
|
87
|
+
profile: parsed.data,
|
|
88
|
+
sources: {
|
|
89
|
+
fields: fieldSources(parsed.data, machine?.raw ?? null, repo?.raw ?? null),
|
|
90
|
+
machine: machine?.label ?? null,
|
|
91
|
+
repo: repo?.label ?? null,
|
|
92
|
+
rules: parsed.data.rules.map((_rule, i) => (i < machineRules ? 'machine' : 'repo')),
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// The repo layer over which the machine layer goes, key by key; `rules` stack, the machine's first.
|
|
97
|
+
export function mergeLayers(machine, repo) {
|
|
98
|
+
const merged = mergeValue(repo, machine);
|
|
99
|
+
if (!isRecord(merged))
|
|
100
|
+
return {};
|
|
101
|
+
const rules = [...listOf(machine.rules), ...listOf(repo.rules)];
|
|
102
|
+
if (machine.rules !== undefined || repo.rules !== undefined)
|
|
103
|
+
merged.rules = rules;
|
|
104
|
+
return merged;
|
|
105
|
+
}
|
|
106
|
+
// The profile of the checkout a verb runs in, as checkoutProfile reads it, under the conductor home
|
|
107
|
+
// `paths` names. Throws a ConductorError when it is no project's checkout or the profile doesn't read.
|
|
108
|
+
export function profileHere(checkout, paths) {
|
|
109
|
+
const layered = checkoutProfile(paths, checkout);
|
|
110
|
+
if (layered === null) {
|
|
111
|
+
throw new ConductorError(`no ${PROFILE_FILE} in ${checkout}`, [
|
|
112
|
+
"run it in a project's worktree or checkout; a project with no committed profile keeps it in ~/.conductor/projects/<name>.json",
|
|
113
|
+
]);
|
|
114
|
+
}
|
|
115
|
+
return layered.profile;
|
|
116
|
+
}
|
|
117
|
+
// `conduct profile <name>`: the files, each field with its value and source, then the rules in the
|
|
118
|
+
// order they are checked, each with its layer.
|
|
119
|
+
export function profileLines(name, { profile, sources }) {
|
|
120
|
+
const rows = Object.keys(sources.fields)
|
|
121
|
+
.sort()
|
|
122
|
+
.map((path) => [path, show(valueAt(profile, path)), sources.fields[path] ?? 'default']);
|
|
123
|
+
const width = Math.max(0, ...rows.map(([path]) => path.length));
|
|
124
|
+
const lines = [
|
|
125
|
+
`profile ${name}`,
|
|
126
|
+
` machine: ${sources.machine ?? '(no machine profile)'}`,
|
|
127
|
+
` repo: ${sources.repo ?? '(no repo profile)'}`,
|
|
128
|
+
'',
|
|
129
|
+
...rows.map(([path, value, source]) => ` ${path.padEnd(width)} ${source.padEnd(7)} ${value}`),
|
|
130
|
+
];
|
|
131
|
+
if (profile.rules.length > 0) {
|
|
132
|
+
lines.push('', "rules (the machine's first: a rule shadows a later one with its id):");
|
|
133
|
+
profile.rules.forEach((rule, i) => {
|
|
134
|
+
const does = typeof rule.do === 'string' ? rule.do : rule.do.join('; ');
|
|
135
|
+
const when = rule.when === undefined ? '' : `when ${rule.when} → `;
|
|
136
|
+
lines.push(` ${(sources.rules[i] ?? 'repo').padEnd(7)} ${rule.id ?? `#${i + 1}`}: ${when}${does}`);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return lines;
|
|
140
|
+
}
|
|
141
|
+
// The profile a daemon-owned clone conducts: the machine file over .claude/conductor.json at
|
|
142
|
+
// origin/<base> in <home>/projects/<name>/repo. The base is the machine's baseBranch, else
|
|
143
|
+
// origin/HEAD's branch; when the repo's file there names another baseBranch, the file is read once
|
|
144
|
+
// more at that one. `running` is this conductor's version, for the layers' `conductor` minimums.
|
|
145
|
+
export function projectProfile(paths, name, git = gitSync, running) {
|
|
146
|
+
const { clone, profile: file } = projectPaths(paths, name);
|
|
147
|
+
if (!existsSync(join(clone, '.git')))
|
|
148
|
+
throw new ConductorError(`${name} has no clone at ${clone}`, ['add it with `conduct project add <url | path>`']);
|
|
149
|
+
const machine = readLayer(file);
|
|
150
|
+
const machineBase = typeof machine?.raw.baseBranch === 'string' ? machine.raw.baseBranch : null;
|
|
151
|
+
let base = machineBase ?? originHead(git, clone);
|
|
152
|
+
let repo = repoLayerAt(git, clone, base);
|
|
153
|
+
const named = typeof repo?.raw.baseBranch === 'string' ? repo.raw.baseBranch : null;
|
|
154
|
+
if (machineBase === null && named !== null && named !== base) {
|
|
155
|
+
base = named;
|
|
156
|
+
repo = repoLayerAt(git, clone, base);
|
|
157
|
+
}
|
|
158
|
+
if (machine === null && repo === null) {
|
|
159
|
+
throw new ConductorError(`${name} has no profile`, [
|
|
160
|
+
`neither ${file} nor ${PROFILE_IN_GIT} at origin/${base} in ${clone}`,
|
|
161
|
+
`write ${file} (a whole profile), or commit ${PROFILE_IN_GIT} to the repo`,
|
|
162
|
+
]);
|
|
163
|
+
}
|
|
164
|
+
return layerProfile(name, { machine, repo }, running);
|
|
165
|
+
}
|
|
166
|
+
// A profile file's text as a layer. Throws a ConductorError naming the file when it isn't a JSONC
|
|
167
|
+
// object.
|
|
168
|
+
export function rawLayer(text, label) {
|
|
169
|
+
return { label, raw: parseJsonc(text, rawSchema, label) };
|
|
170
|
+
}
|
|
171
|
+
// A profile file as a layer; null when there is no file.
|
|
172
|
+
export function readLayer(file) {
|
|
173
|
+
let text;
|
|
174
|
+
try {
|
|
175
|
+
text = readFileSync(file, 'utf8');
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
if (isMissing(error))
|
|
179
|
+
return null;
|
|
180
|
+
throw new ConductorError(`could not read ${file}`, [String(error)]);
|
|
181
|
+
}
|
|
182
|
+
return rawLayer(text, file);
|
|
183
|
+
}
|
|
184
|
+
// .claude/conductor.json at origin/<base> in a clone, or null when that commit has none.
|
|
185
|
+
export function repoLayerAt(git, clone, base) {
|
|
186
|
+
const ref = `origin/${base}`;
|
|
187
|
+
if (git(['rev-parse', '--verify', '--quiet', `${ref}^{commit}`], clone).code !== 0) {
|
|
188
|
+
throw new ConductorError(`${clone} has no ${ref}`, [
|
|
189
|
+
`check baseBranch ("${base}"); the conductor fetches the clone every few minutes`,
|
|
190
|
+
]);
|
|
191
|
+
}
|
|
192
|
+
const spec = `${ref}:${PROFILE_IN_GIT}`;
|
|
193
|
+
if (git(['cat-file', '-e', spec], clone).code !== 0)
|
|
194
|
+
return null;
|
|
195
|
+
const shown = git(['show', spec], clone);
|
|
196
|
+
if (shown.code !== 0)
|
|
197
|
+
throw new ConductorError(`git show ${spec} failed in ${clone}`, [shown.stderr.trim()]);
|
|
198
|
+
return rawLayer(shown.stdout, `${PROFILE_IN_GIT} at ${ref} in ${clone}`);
|
|
199
|
+
}
|
|
200
|
+
// Each field's source, by its dotted path in the profile as read: the machine's when its file
|
|
201
|
+
// writes the field, else the repo's, else the default's. Arrays and `rules` count as one value;
|
|
202
|
+
// `name` is the registration's, which only a repo file of the same name can claim.
|
|
203
|
+
function fieldSources(profile, machine, repo) {
|
|
204
|
+
const fields = {};
|
|
205
|
+
const walk = (value, path) => {
|
|
206
|
+
if (isRecord(value) && Object.keys(value).length > 0) {
|
|
207
|
+
for (const [key, child] of Object.entries(value))
|
|
208
|
+
walk(child, [...path, key]);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
fields[path.join('.')] = has(machine, path) ? 'machine' : has(repo, path) ? 'repo' : 'default';
|
|
212
|
+
};
|
|
213
|
+
for (const [key, value] of Object.entries(profile))
|
|
214
|
+
if (key !== 'rules')
|
|
215
|
+
walk(value, [key]);
|
|
216
|
+
fields.name = repo?.name === profile.name && machine?.name === undefined ? 'repo' : 'machine';
|
|
217
|
+
return fields;
|
|
218
|
+
}
|
|
219
|
+
function has(raw, path) {
|
|
220
|
+
let at = raw;
|
|
221
|
+
for (const key of path) {
|
|
222
|
+
if (!isRecord(at) || !Object.hasOwn(at, key))
|
|
223
|
+
return false;
|
|
224
|
+
at = at[key];
|
|
225
|
+
}
|
|
226
|
+
return at !== undefined;
|
|
227
|
+
}
|
|
228
|
+
function isRecord(value) {
|
|
229
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
230
|
+
}
|
|
231
|
+
function listOf(value) {
|
|
232
|
+
return Array.isArray(value) ? value : [];
|
|
233
|
+
}
|
|
234
|
+
function mergeValue(under, over) {
|
|
235
|
+
if (over === undefined)
|
|
236
|
+
return under;
|
|
237
|
+
if (!isRecord(under) || !isRecord(over))
|
|
238
|
+
return over;
|
|
239
|
+
const merged = { ...under };
|
|
240
|
+
for (const [key, value] of Object.entries(over))
|
|
241
|
+
merged[key] = mergeValue(under[key], value);
|
|
242
|
+
return merged;
|
|
243
|
+
}
|
|
244
|
+
// origin/HEAD's branch in a clone: the remote's default branch, which a clone records.
|
|
245
|
+
function originHead(git, clone) {
|
|
246
|
+
const head = git(['symbolic-ref', '--short', 'refs/remotes/origin/HEAD'], clone);
|
|
247
|
+
const branch = head.code === 0 ? head.stdout.trim().replace(/^origin\//, '') : '';
|
|
248
|
+
if (branch === '') {
|
|
249
|
+
throw new ConductorError(`${clone} has no origin/HEAD`, [
|
|
250
|
+
`run git -C "${clone}" remote set-head origin --auto, or set baseBranch in the machine profile`,
|
|
251
|
+
]);
|
|
252
|
+
}
|
|
253
|
+
return branch;
|
|
254
|
+
}
|
|
255
|
+
function show(value) {
|
|
256
|
+
return typeof value === 'string' ? value : (JSON.stringify(value) ?? '(unset)');
|
|
257
|
+
}
|
|
258
|
+
function valueAt(profile, path) {
|
|
259
|
+
let at = profile;
|
|
260
|
+
for (const key of path.split('.'))
|
|
261
|
+
at = isRecord(at) ? at[key] : undefined;
|
|
262
|
+
return at;
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=profile-layers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-layers.js","sourceRoot":"","sources":["../src/profile-layers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAuB,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC5F,OAAO,EAAgB,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,2EAA2E;AAC3E,oGAAoG;AACpG,mGAAmG;AACnG,oGAAoG;AACpG,wFAAwF;AAExF,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,SAAS,EAAE,MAAM,CAAU,CAAC;AAGnD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,iGAAiG;IACjG,0DAA0D;IAC1D,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;IAC5D,0CAA0C;IAC1C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,+FAA+F;IAC/F,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,6DAA6D;IAC7D,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;CAC/B,CAAC,CAAC;AAqBH,MAAM,CAAC,MAAM,OAAO,GAAY,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC5C,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IACrG,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;KAC5B,CAAC;AACJ,CAAC,CAAC;AAEF,+FAA+F;AAC/F,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAEhD,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAEpD,2FAA2F;AAC3F,iGAAiG;AACjG,mGAAmG;AACnG,8FAA8F;AAC9F,yDAAyD;AACzD,MAAM,UAAU,eAAe,CAAC,KAAuC,EAAE,QAAgB;IACvF,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3G,8DAA8D;IAC9D,MAAM,OAAO,GAAG,IAAI,KAAK,IAAI,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/G,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACtE,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,kGAAkG;AAClG,mGAAmG;AACnG,mEAAmE;AACnE,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,MAAmE,EACnE,OAAgB;IAEhB,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;IACjC,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAqB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1D,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACpF,MAAM,IAAI,cAAc,CAAC,GAAG,OAAO,CAAC,KAAK,sBAAsB,EAAE;YAC/D,SAAS,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,6DAA6D,IAAI,QAAQ;SACnI,CAAC,CAAC;IACL,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,OAAO,KAAK,CAAC,GAAG,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5F,IAAI,OAAO,KAAK,IAAI;YAAE,SAAS;QAC/B,MAAM,OAAO,GAAG,OAAO,IAAI,WAAW,EAAE,CAAC,OAAO,CAAC;QACjD,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;YAAE,MAAM,IAAI,kBAAkB,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IAChE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,cAAc,CAAC,GAAG,MAAM,aAAa,EAAE;YAC/C,aAAa,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,qDAAqD,QAAQ,EAAE;SACpH,CAAC,CAAC;IACL,CAAC;IACD,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,cAAc,CAAC,GAAG,MAAM,aAAa,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAChG,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACtF,OAAO;QACL,MAAM;QACN,OAAO,EAAE,MAAM,CAAC,IAAI;QACpB,OAAO,EAAE;YACP,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC;YAC1E,OAAO,EAAE,OAAO,EAAE,KAAK,IAAI,IAAI;YAC/B,IAAI,EAAE,IAAI,EAAE,KAAK,IAAI,IAAI;YACzB,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;SACpF;KACF,CAAC;AACJ,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,WAAW,CAAC,OAAgC,EAAE,IAA6B;IACzF,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACzC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IAClF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,oGAAoG;AACpG,uGAAuG;AACvG,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,KAAuC;IACnF,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACjD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,cAAc,CAAC,MAAM,YAAY,OAAO,QAAQ,EAAE,EAAE;YAC5D,+HAA+H;SAChI,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,CAAC;AACzB,CAAC;AAED,mGAAmG;AACnG,+CAA+C;AAC/C,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,EAAE,OAAO,EAAE,OAAO,EAAkB;IAC7E,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;SACrC,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAU,CAAC,CAAC;IACnG,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG;QACZ,WAAW,IAAI,EAAE;QACjB,cAAc,OAAO,CAAC,OAAO,IAAI,sBAAsB,EAAE;QACzD,cAAc,OAAO,CAAC,IAAI,IAAI,mBAAmB,EAAE;QACnD,EAAE;QACF,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;KACjG,CAAC;IACF,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,sEAAsE,CAAC,CAAC;QACvF,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YAChC,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,IAAI,KAAK,CAAC;YACnE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;QACvG,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,6FAA6F;AAC7F,2FAA2F;AAC3F,mGAAmG;AACnG,iGAAiG;AACjG,MAAM,UAAU,cAAc,CAC5B,KAAuC,EACvC,IAAY,EACZ,MAAe,OAAO,EACtB,OAAgB;IAEhB,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAClC,MAAM,IAAI,cAAc,CAAC,GAAG,IAAI,oBAAoB,KAAK,EAAE,EAAE,CAAC,gDAAgD,CAAC,CAAC,CAAC;IACnH,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,WAAW,GAAG,OAAO,OAAO,EAAE,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IAChG,IAAI,IAAI,GAAG,WAAW,IAAI,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACjD,IAAI,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,OAAO,IAAI,EAAE,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IACpF,IAAI,WAAW,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC7D,IAAI,GAAG,KAAK,CAAC;QACb,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;IACD,IAAI,OAAO,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QACtC,MAAM,IAAI,cAAc,CAAC,GAAG,IAAI,iBAAiB,EAAE;YACjD,WAAW,IAAI,QAAQ,cAAc,cAAc,IAAI,OAAO,KAAK,EAAE;YACrE,SAAS,IAAI,iCAAiC,cAAc,cAAc;SAC3E,CAAC,CAAC;IACL,CAAC;IACD,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;AACxD,CAAC;AAED,kGAAkG;AAClG,UAAU;AACV,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,KAAa;IAClD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;AAC5D,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,SAAS,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAClC,MAAM,IAAI,cAAc,CAAC,kBAAkB,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,WAAW,CAAC,GAAY,EAAE,KAAa,EAAE,IAAY;IACnE,MAAM,GAAG,GAAG,UAAU,IAAI,EAAE,CAAC;IAC7B,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,GAAG,WAAW,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACnF,MAAM,IAAI,cAAc,CAAC,GAAG,KAAK,WAAW,GAAG,EAAE,EAAE;YACjD,sBAAsB,IAAI,uDAAuD;SAClF,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,GAAG,IAAI,cAAc,EAAE,CAAC;IACxC,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACjE,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;IACzC,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;QAAE,MAAM,IAAI,cAAc,CAAC,YAAY,IAAI,cAAc,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7G,OAAO,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,cAAc,OAAO,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC;AAC3E,CAAC;AAED,8FAA8F;AAC9F,gGAAgG;AAChG,mFAAmF;AACnF,SAAS,YAAY,CACnB,OAAgB,EAChB,OAAuC,EACvC,IAAoC;IAEpC,MAAM,MAAM,GAAsC,EAAE,CAAC;IACrD,MAAM,IAAI,GAAG,CAAC,KAAc,EAAE,IAAc,EAAQ,EAAE;QACpD,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;YAC9E,OAAO;QACT,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IACjG,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,IAAI,GAAG,KAAK,OAAO;YAAE,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5F,MAAM,CAAC,IAAI,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,IAAI,OAAO,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9F,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,GAAG,CAAC,GAAmC,EAAE,IAAuB;IACvE,IAAI,EAAE,GAAY,GAAG,CAAC;IACtB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAC3D,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;IACD,OAAO,EAAE,KAAK,SAAS,CAAC;AAC1B,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3C,CAAC;AAED,SAAS,UAAU,CAAC,KAAc,EAAE,IAAa;IAC/C,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACrC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACrD,MAAM,MAAM,GAA4B,EAAE,GAAG,KAAK,EAAE,CAAC;IACrD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,MAAM,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7F,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,uFAAuF;AACvF,SAAS,UAAU,CAAC,GAAY,EAAE,KAAa;IAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,cAAc,EAAE,SAAS,EAAE,0BAA0B,CAAC,EAAE,KAAK,CAAC,CAAC;IACjF,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClF,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,cAAc,CAAC,GAAG,KAAK,qBAAqB,EAAE;YACtD,eAAe,KAAK,2EAA2E;SAChG,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,IAAI,CAAC,KAAc;IAC1B,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,CAAC;AAClF,CAAC;AAED,SAAS,OAAO,CAAC,OAAgB,EAAE,IAAY;IAC7C,IAAI,EAAE,GAAY,OAAO,CAAC;IAC1B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;QAAE,EAAE,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
package/build/profile.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isAbsolute, join } from 'node:path';
|
|
2
|
+
import { lt, valid } from 'semver';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { ConductorError
|
|
4
|
+
import { ConductorError } from "./errors.js";
|
|
5
5
|
import { parseJsonc } from "./jsonc.js";
|
|
6
|
+
import { packageInfo } from "./package-info.js";
|
|
7
|
+
import { PROJECT_NAME } from "./paths.js";
|
|
6
8
|
import { noteName } from "./plugin.js";
|
|
7
9
|
import { PROTOCOL } from "./protocol.js";
|
|
10
|
+
import { GATES } from "./release-kinds.js";
|
|
8
11
|
import { ruleInputSchema } from "./rule-input.js";
|
|
12
|
+
import { defaultTag } from "./upgrade.js";
|
|
9
13
|
export const PROFILE_FILE = join('.claude', 'conductor.json');
|
|
10
|
-
const name = z.string().regex(
|
|
14
|
+
const name = z.string().regex(PROJECT_NAME, 'lowercase letters, digits and dashes, starting with a letter');
|
|
11
15
|
const text = z.string().trim().min(1);
|
|
12
|
-
const port = z.int().min(1).max(65535);
|
|
13
16
|
// The state each phase note moves an item to, over the tracker kind's defaults (tracker-writes.ts);
|
|
14
17
|
// null leaves it as it is.
|
|
15
18
|
const transitions = z
|
|
@@ -48,33 +51,50 @@ const profileFields = z.strictObject({
|
|
|
48
51
|
repo: z.string().regex(/^[\w.-]+$/, 'a GitHub repo name, without the owner'),
|
|
49
52
|
}),
|
|
50
53
|
]),
|
|
51
|
-
// The
|
|
52
|
-
|
|
54
|
+
// The oldest conductor that reads this profile (ATK-573): the version that first read its newest
|
|
55
|
+
// field (profile-fields.ts records each one's), a -dev.N while that is only on `dev`. parseProfile
|
|
56
|
+
// checks it before the rest, so an older conductor says which version to upgrade to instead of
|
|
57
|
+
// listing fields it doesn't know.
|
|
58
|
+
conductor: z
|
|
59
|
+
.string()
|
|
60
|
+
.refine((v) => valid(v) !== null, 'a version, such as 3.0.0-dev.8')
|
|
61
|
+
.optional(),
|
|
62
|
+
// The kinds of item that wait on a person in this project: plan, merge and each release kind's gate
|
|
63
|
+
// (release-kinds.ts: npm-otp for an npm-stage), which `conduct project check` looks for.
|
|
64
|
+
gates: z.array(z.enum(GATES)).min(1),
|
|
53
65
|
// Each environment's /health URL, for `conduct wait health <env>`.
|
|
54
66
|
health: z.record(name, z.url()).optional(),
|
|
67
|
+
// A checkout the operator keeps whose gitignored files matching its .worktreeinclude are copied
|
|
68
|
+
// into each new worktree (design note v2, section 2), for local files `setup` can't build. An
|
|
69
|
+
// absolute path; off when absent.
|
|
70
|
+
localFilesFrom: text.refine((path) => isAbsolute(path), 'an absolute path to a checkout').optional(),
|
|
55
71
|
name,
|
|
56
72
|
// The package `conduct wait npm <version>` means.
|
|
57
73
|
npm: z.strictObject({ package: text }).optional(),
|
|
58
|
-
// The dev servers
|
|
59
|
-
//
|
|
74
|
+
// The ports a worktree's dev servers need, by name, such as ["web", "api"]. The port broker gives
|
|
75
|
+
// each worktree its own from the machine's pool, as CONDUCTOR_PORT_<NAME> to `setup` and the worker.
|
|
60
76
|
ports: z
|
|
61
|
-
.
|
|
62
|
-
.
|
|
63
|
-
.
|
|
64
|
-
// A misspelt maxSlot would otherwise read as a port named "maxslot".
|
|
65
|
-
.refine((ports) => Object.keys(ports).every((key) => key === 'maxSlot' || !key.toLowerCase().replace(/[-_]/g, '').startsWith('maxslot')), 'maxSlot is spelt "maxSlot"'),
|
|
77
|
+
.array(z.string().regex(/^[a-z][a-z0-9_]*$/, 'lowercase letters, digits and _, starting with a letter'))
|
|
78
|
+
.refine((names) => new Set(names).size === names.length, 'each port name once')
|
|
79
|
+
.default([]),
|
|
66
80
|
// The worker protocol the project's work skill speaks (protocol.ts); only this conductor's.
|
|
67
81
|
protocol: z.literal(PROTOCOL),
|
|
68
82
|
release: z.strictObject({ handoff: text }),
|
|
69
|
-
// The pipelines that stage a release for a merge (`conduct runs`, `wait merge`)
|
|
70
|
-
// `package` on npm for a person's `npm stage approve`.
|
|
71
|
-
// file name (release-cli), path or own name.
|
|
83
|
+
// The pipelines that stage a release for a merge (`conduct runs`, `wait merge`), each of a kind
|
|
84
|
+
// release-kinds.ts reads: `npm-stage` stages `package` on npm for a person's `npm stage approve`.
|
|
85
|
+
// `pipeline` names it as --for does: its YAML file name (release-cli), path or own name.
|
|
72
86
|
releases: z
|
|
73
|
-
.array(z.
|
|
87
|
+
.array(z.discriminatedUnion('kind', [
|
|
88
|
+
z.strictObject({ key: name, kind: z.literal('npm-stage'), package: text, pipeline: text }),
|
|
89
|
+
]))
|
|
74
90
|
.default([]),
|
|
75
91
|
// Project rules (design note, section 9): checked before the machine's and the defaults, for
|
|
76
92
|
// this project's items only. They change by PR, and the daemon reloads them on save.
|
|
77
93
|
rules: z.array(ruleInputSchema).default([]),
|
|
94
|
+
// Shell commands run in order in each new worktree, after `localFilesFrom`'s copy (design note v2,
|
|
95
|
+
// section 2): they build its gitignored files (installs, local settings) from what the repo commits.
|
|
96
|
+
// The first that fails fails the launch.
|
|
97
|
+
setup: z.array(text).default([]),
|
|
78
98
|
// `aliases`: older names of the work skill. Notes headed with one still count, so an item in
|
|
79
99
|
// flight when the work skill is renamed keeps its phases, approvals and review threads.
|
|
80
100
|
skills: z.strictObject({ aliases: z.array(text).default([]), verify: text, work: text }),
|
|
@@ -176,8 +196,8 @@ const profileFields = z.strictObject({
|
|
|
176
196
|
.prefault({}),
|
|
177
197
|
})
|
|
178
198
|
.prefault({}),
|
|
179
|
-
//
|
|
180
|
-
//
|
|
199
|
+
// Moved to the machine's config.json (workers.tab, ATK-529), and ignored here. Still read, so a
|
|
200
|
+
// profile written for an older conductor isn't refused; `conduct project check` warns about it.
|
|
181
201
|
workers: z.strictObject({ tab: z.boolean().default(false) }).prefault({}),
|
|
182
202
|
});
|
|
183
203
|
// ADO Boards takes its organization and project from an Azure DevOps code host, and GitHub Issues
|
|
@@ -215,11 +235,6 @@ export function adoOrganization(profile) {
|
|
|
215
235
|
return profile.codeHost.organization;
|
|
216
236
|
return profile.tracker.kind === 'ado-boards' ? (profile.tracker.organization ?? null) : null;
|
|
217
237
|
}
|
|
218
|
-
// The profile's base ports by name, without maxSlot.
|
|
219
|
-
export function basePorts(profile) {
|
|
220
|
-
const { maxSlot: _maxSlot, ...ports } = profile.ports;
|
|
221
|
-
return ports;
|
|
222
|
-
}
|
|
223
238
|
// The `<skill> · ` headings that mark a project's notes: its work skill's, then each alias's.
|
|
224
239
|
// Without a profile, work-issue's, which every project used before profiles named their own. The
|
|
225
240
|
// conductor plugin's skills head their notes with the bare name: `work-item · `, not
|
|
@@ -233,11 +248,30 @@ export function notePrefixes(profile) {
|
|
|
233
248
|
export function trackerKey(profile) {
|
|
234
249
|
return profile.tracker.kind === 'linear' ? profile.tracker.team : profile.tracker.key;
|
|
235
250
|
}
|
|
236
|
-
const
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
export
|
|
240
|
-
|
|
251
|
+
const versionsOnly = z.looseObject({ conductor: z.unknown().optional(), protocol: z.unknown().optional() });
|
|
252
|
+
// A profile whose `conductor` minimum is newer than this conductor. `upgrade` brings the machine up
|
|
253
|
+
// to it: from `dev` for a -dev.N minimum, else from `latest`.
|
|
254
|
+
export class ProfileTooNewError extends ConductorError {
|
|
255
|
+
upgrade;
|
|
256
|
+
constructor(source, minimum, running) {
|
|
257
|
+
const upgrade = `conduct upgrade --tag ${defaultTag(minimum)}`;
|
|
258
|
+
super(`${source} is invalid`, [`conductor: needs ${minimum} or newer; this is ${running}: run \`${upgrade}\``]);
|
|
259
|
+
this.name = 'ProfileTooNewError';
|
|
260
|
+
this.upgrade = upgrade;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// The conductor minimum, then the protocol, are checked first, so a profile written for another
|
|
264
|
+
// version gets one clear message instead of a list of schema differences. `running` is this
|
|
265
|
+
// conductor's version, read only for a profile with a minimum.
|
|
266
|
+
export function parseProfile(text, source, running) {
|
|
267
|
+
const { conductor, protocol } = parseJsonc(text, versionsOnly, source);
|
|
268
|
+
// As semver reads it: `v3.0.0-dev.9` and `3.0.0-dev.9+abc` both mean 3.0.0-dev.9.
|
|
269
|
+
const minimum = typeof conductor === 'string' ? valid(conductor) : null;
|
|
270
|
+
if (minimum !== null) {
|
|
271
|
+
const version = running ?? packageInfo().version;
|
|
272
|
+
if (lt(version, minimum))
|
|
273
|
+
throw new ProfileTooNewError(source, minimum, version);
|
|
274
|
+
}
|
|
241
275
|
if (protocol !== PROTOCOL) {
|
|
242
276
|
throw new ConductorError(`${source} is invalid`, [
|
|
243
277
|
`protocol: ${JSON.stringify(protocol ?? null)} is not supported; this conductor speaks protocol ${PROTOCOL}`,
|
|
@@ -245,19 +279,11 @@ export function parseProfile(text, source) {
|
|
|
245
279
|
}
|
|
246
280
|
return parseJsonc(text, profileSchema, source);
|
|
247
281
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
catch (error) {
|
|
255
|
-
if (!isMissing(error))
|
|
256
|
-
throw error;
|
|
257
|
-
throw new ConductorError(`no ${PROFILE_FILE} in ${root}`, [
|
|
258
|
-
'a conducted project commits its profile at .claude/conductor.json',
|
|
259
|
-
]);
|
|
260
|
-
}
|
|
261
|
-
return { path, profile: parseProfile(source, path) };
|
|
282
|
+
// Why a profile didn't read, in one line: a ConductorError's problems after its file line (the
|
|
283
|
+
// version it needs, the fields), so a verb that turns it into its own refusal keeps them.
|
|
284
|
+
export function profileProblem(error) {
|
|
285
|
+
if (!(error instanceof ConductorError))
|
|
286
|
+
return error instanceof Error ? error.message : String(error);
|
|
287
|
+
return error.details.length > 0 ? error.details.join('; ') : error.message;
|
|
262
288
|
}
|
|
263
289
|
//# sourceMappingURL=profile.js.map
|