overlord-cli 0.2606041226.0 → 0.2606050717.0
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/README.md +2 -0
- package/bin/_cli/index.mjs +7 -0
- package/bin/_cli/onboard.mjs +40 -10
- package/bin/_cli/protocol.mjs +150 -0
- package/bin/_cli/setup.mjs +127 -8
- package/package.json +1 -1
- package/plugins/claude/skills/overlord-ticket/SKILL.md +1 -1
- package/plugins/claude/skills/overlord-ticket/reference/cli.md +23 -0
- package/plugins/claude/skills/overlord-ticket/reference/devices.md +1 -1
- package/plugins/cursor/skills/overlord-ticket/SKILL.md +1 -1
- package/plugins/cursor/skills/overlord-ticket/reference/cli.md +23 -0
- package/plugins/cursor/skills/overlord-ticket/reference/devices.md +1 -1
- package/plugins/overlord/scripts/overlord-mcp.mjs +55 -0
- package/plugins/overlord/skills/overlord-ticket/SKILL.md +1 -1
- package/plugins/overlord/skills/overlord-ticket/reference/cli.md +23 -0
- package/plugins/overlord/skills/overlord-ticket/reference/devices.md +1 -1
package/README.md
CHANGED
|
@@ -107,6 +107,7 @@ Top-level commands (see `ovld help`):
|
|
|
107
107
|
- `<agent> "<prompt>"` - create a ticket from the prompt (project inferred from the current directory) and launch the agent on it in one step, e.g. `ovld claude "fix the flaky login test" --model opus`. Built-in agents need an installed connector (`ovld setup <agent>`, override with `--allow-uninstalled`); your custom agents launch by id. Flags after a standalone `--` pass through to the agent binary.
|
|
108
108
|
- `attach` - search tickets and launch an agent interactively (`ovld attach [ticketId] [agent]`)
|
|
109
109
|
- `create` - create a ticket with numbered project selection; supports `--agent`, `--model`, `--delegate` (same delegate flags as `ovld protocol create`)
|
|
110
|
+
- `create-project` - create a project, registering the current directory (or `--directory`) as its primary resource in one step; `--no-directory` makes a bare project (alias for `ovld protocol create-project`)
|
|
110
111
|
- `prompt` - create a ticket, then launch an agent on it
|
|
111
112
|
- `auth` - `login`, `status`, `repair` (shared Desktop/CLI credentials), or `logout`
|
|
112
113
|
- `tickets` - `create` or `list` (optional `--status`)
|
|
@@ -150,6 +151,7 @@ Agents can find docs here: https://www.ovld.ai/docs/for-agents
|
|
|
150
151
|
- `attachment-download-url` - get a signed download URL for an existing objective attachment
|
|
151
152
|
- `attachment-upload-file` - prepare, upload, and finalize a local objective attachment in one command
|
|
152
153
|
- `get-device` / `update-device` - register the caller machine (per organization + user + fingerprint) and rename its label
|
|
154
|
+
- `create-project` - create a project and, when a `--directory` + `--device-fingerprint` are given, register it as the project's primary resource in one step
|
|
153
155
|
- `list-project-resources` / `add-project-resource` / `update-project-resource` - manage filesystem directories tied to a project device
|
|
154
156
|
- `request-execution` / `claim-execution` / `list-execution-requests` / `clear-execution-requests` / `complete-execution-launch` / `fail-execution-launch` - durable runner queue operations used by auto-advance and manual Run
|
|
155
157
|
- `list-organizations` - list every organization the authenticated user (or agent token) belongs to; the runner uses this to poll all of your organizations, not just the one stored at login
|
package/bin/_cli/index.mjs
CHANGED
|
@@ -33,6 +33,7 @@ Usage:
|
|
|
33
33
|
${primaryCommand} attach [ticketId] [agent] Search tickets and launch an agent (interactive)
|
|
34
34
|
${primaryCommand} create "<objective>" Create a ticket with numbered project selection; supports --agent/--model/--delegate
|
|
35
35
|
${primaryCommand} prompt "<objective>" Create a ticket, then launch an agent on it
|
|
36
|
+
${primaryCommand} create-project --name "<name>" Create a project, optionally linking a directory in one step
|
|
36
37
|
${primaryCommand} add-cwd Register the current directory as a project resource (interactive)
|
|
37
38
|
${primaryCommand} onboard Create an account, project, cwd link, and onboarding ticket
|
|
38
39
|
${primaryCommand} auth <subcommand> Login, logout, repair, or check auth status
|
|
@@ -112,6 +113,12 @@ export async function runCli({ primaryCommand }) {
|
|
|
112
113
|
return;
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
if (command === 'create-project') {
|
|
117
|
+
// Friendly top-level alias for `ovld protocol create-project`.
|
|
118
|
+
await runProtocolCommand('create-project', rest);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
115
122
|
if (command === 'add-cwd') {
|
|
116
123
|
const { runAddCwdCommand } = await import('./add-cwd.mjs');
|
|
117
124
|
await runAddCwdCommand(rest);
|
package/bin/_cli/onboard.mjs
CHANGED
|
@@ -105,14 +105,27 @@ async function promptForYes(question, defaultYes = true) {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
function buildSignupUrl(platformUrl, verificationUri, name) {
|
|
108
|
+
function buildSignupUrl(platformUrl, verificationUri, name, invite) {
|
|
109
109
|
const verificationUrl = new URL(verificationUri);
|
|
110
110
|
const signupUrl = new URL('/signup', platformUrl);
|
|
111
111
|
signupUrl.searchParams.set('next', `${verificationUrl.pathname}${verificationUrl.search}`);
|
|
112
112
|
if (name) signupUrl.searchParams.set('name', name);
|
|
113
|
+
// Pre-fill the invited email and accept the invite after signup (web fallback parity).
|
|
114
|
+
if (invite) signupUrl.searchParams.set('invite', invite);
|
|
113
115
|
return signupUrl.toString();
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Accept either a bare invitation token or a full `…/invite/<token>` URL pasted
|
|
120
|
+
* from the invite email, returning the bare token (or '' when not provided).
|
|
121
|
+
*/
|
|
122
|
+
function normalizeInviteToken(raw) {
|
|
123
|
+
const value = typeof raw === 'string' ? raw.trim() : '';
|
|
124
|
+
if (!value) return '';
|
|
125
|
+
const match = value.match(/\/invite\/([^/?#]+)/);
|
|
126
|
+
return match ? decodeURIComponent(match[1]) : value;
|
|
127
|
+
}
|
|
128
|
+
|
|
116
129
|
async function completeCliOnboarding(platformUrl, localSecret, credentials, payload) {
|
|
117
130
|
const res = await fetch(`${platformUrl}/api/auth/cli-onboarding`, {
|
|
118
131
|
method: 'POST',
|
|
@@ -141,7 +154,7 @@ export async function runOnboardCommand(args) {
|
|
|
141
154
|
const flags = parseFlags(args);
|
|
142
155
|
|
|
143
156
|
if (flags.help === true || flags.h === true || args[0] === 'help') {
|
|
144
|
-
console.log(`Usage: ovld onboard [--name <name>] [--organization-name <name>] [--project-name <name>] [--directory <path>] [--yes] [--no-desktop]
|
|
157
|
+
console.log(`Usage: ovld onboard [--name <name>] [--organization-name <name>] [--project-name <name>] [--directory <path>] [--invite <token|url>] [--yes] [--no-desktop]
|
|
145
158
|
|
|
146
159
|
Creates an Overlord account setup from the terminal:
|
|
147
160
|
1. collects your name, organization, project, and repository directory
|
|
@@ -149,21 +162,32 @@ Creates an Overlord account setup from the terminal:
|
|
|
149
162
|
3. creates the organization/project, links this directory, and creates the first onboarding ticket
|
|
150
163
|
4. opens the Desktop download page, or the web project when --no-desktop is used
|
|
151
164
|
|
|
165
|
+
With --invite <token>, joins the inviting organization (from an invite email)
|
|
166
|
+
with the invited role instead of creating a new organization; the organization
|
|
167
|
+
prompt is skipped. Accepts a bare token or a full /invite/<token> URL.
|
|
168
|
+
|
|
152
169
|
Run this from the repository you want Overlord agents to work in.`);
|
|
153
170
|
return;
|
|
154
171
|
}
|
|
155
172
|
|
|
173
|
+
const inviteToken = normalizeInviteToken(flags.invite ?? flags['invite-code']);
|
|
174
|
+
|
|
156
175
|
const directoryPath = path.resolve(trimFlag(flags, 'directory') || process.cwd());
|
|
157
176
|
const directoryName = path.basename(directoryPath) || 'My project';
|
|
158
177
|
const inferredName = os.userInfo().username || 'User';
|
|
159
178
|
const name = trimFlag(flags, 'name') || (await promptForValue('Your name', inferredName));
|
|
160
|
-
|
|
161
|
-
|
|
179
|
+
// On the invite path the org comes from the invitation, so skip the org prompt.
|
|
180
|
+
const organizationName = inviteToken
|
|
181
|
+
? trimFlag(flags, 'organization-name')
|
|
182
|
+
: trimFlag(flags, 'organization-name') || (await promptForValue('Organization name', name));
|
|
162
183
|
const projectName =
|
|
163
184
|
trimFlag(flags, 'project-name') || (await promptForValue('Project name', directoryName));
|
|
164
185
|
|
|
165
|
-
if (!name || !
|
|
166
|
-
throw new Error('Name
|
|
186
|
+
if (!name || !projectName) {
|
|
187
|
+
throw new Error('Name and project name are required.');
|
|
188
|
+
}
|
|
189
|
+
if (!inviteToken && !organizationName) {
|
|
190
|
+
throw new Error('Organization name is required (or pass --invite <token> to join an org).');
|
|
167
191
|
}
|
|
168
192
|
|
|
169
193
|
const storedCredentials = loadCredentials();
|
|
@@ -176,7 +200,7 @@ Run this from the repository you want Overlord agents to work in.`);
|
|
|
176
200
|
|
|
177
201
|
const credentials = await authLoginViaDeviceFlow(platformUrl, localSecret, {
|
|
178
202
|
browserOpener: verificationUri => {
|
|
179
|
-
const signupUrl = buildSignupUrl(platformUrl, verificationUri, name);
|
|
203
|
+
const signupUrl = buildSignupUrl(platformUrl, verificationUri, name, inviteToken);
|
|
180
204
|
console.log(` Signup URL: ${signupUrl}`);
|
|
181
205
|
openBrowser(signupUrl);
|
|
182
206
|
}
|
|
@@ -186,12 +210,13 @@ Run this from the repository you want Overlord agents to work in.`);
|
|
|
186
210
|
const deviceFingerprint = readOrCreateDeviceFingerprint(flags);
|
|
187
211
|
const result = await completeCliOnboarding(resolvedPlatformUrl, localSecret, credentials, {
|
|
188
212
|
name,
|
|
189
|
-
organizationName,
|
|
213
|
+
...(organizationName ? { organizationName } : {}),
|
|
190
214
|
projectName,
|
|
191
215
|
directoryPath,
|
|
192
216
|
deviceFingerprint,
|
|
193
217
|
deviceHostname: os.hostname(),
|
|
194
|
-
devicePlatform: process.platform
|
|
218
|
+
devicePlatform: process.platform,
|
|
219
|
+
...(inviteToken ? { inviteToken } : {})
|
|
195
220
|
});
|
|
196
221
|
|
|
197
222
|
saveCredentials({
|
|
@@ -216,9 +241,14 @@ Run this from the repository you want Overlord agents to work in.`);
|
|
|
216
241
|
|
|
217
242
|
console.log('\nOverlord setup complete.');
|
|
218
243
|
console.log(` Organization: ${result.organization.organizationName}`);
|
|
244
|
+
if (result.organization.role) {
|
|
245
|
+
console.log(` Role: ${result.organization.role}`);
|
|
246
|
+
}
|
|
219
247
|
console.log(` Project: ${result.project.name}`);
|
|
220
248
|
console.log(` Directory: ${directoryPath}`);
|
|
221
|
-
|
|
249
|
+
if (result.ticket) {
|
|
250
|
+
console.log(` Onboarding ticket: ${result.ticket.reference ?? result.ticket.id}`);
|
|
251
|
+
}
|
|
222
252
|
|
|
223
253
|
const skipDesktop = flags['no-desktop'] === true || flags.desktop === 'false';
|
|
224
254
|
const autoYes = flags.yes === true || flags.y === true;
|
package/bin/_cli/protocol.mjs
CHANGED
|
@@ -1982,6 +1982,135 @@ async function protocolUpdateProjectResource(args) {
|
|
|
1982
1982
|
console.log(JSON.stringify(data, null, 2));
|
|
1983
1983
|
}
|
|
1984
1984
|
|
|
1985
|
+
// ---------------------------------------------------------------------------
|
|
1986
|
+
// create-project (create a project, optionally registering a primary directory)
|
|
1987
|
+
// ---------------------------------------------------------------------------
|
|
1988
|
+
|
|
1989
|
+
async function protocolCreateProject(args) {
|
|
1990
|
+
const flags = parseFlags(args);
|
|
1991
|
+
|
|
1992
|
+
if (flags.help === true || flags.h === true) {
|
|
1993
|
+
console.log(`Usage: ovld create-project --name "<name>" [options]
|
|
1994
|
+
|
|
1995
|
+
Create a project. By default the current working directory is registered as the
|
|
1996
|
+
project's primary resource in the same call (one-step setup).
|
|
1997
|
+
|
|
1998
|
+
Options:
|
|
1999
|
+
--name <text> Project name (required)
|
|
2000
|
+
--color <#rrggbb> Project color (defaults to a soft rose)
|
|
2001
|
+
--directory <path> Directory to register as the primary resource
|
|
2002
|
+
(defaults to the current working directory)
|
|
2003
|
+
--no-directory Create a bare project without registering a directory
|
|
2004
|
+
--device-fingerprint <fp> Device for the directory (or OVERLORD_DEVICE_FINGERPRINT;
|
|
2005
|
+
auto-generated when omitted)
|
|
2006
|
+
--label <text> Label for the registered directory
|
|
2007
|
+
--is-primary <true|false> Override primary status (directory defaults to primary)
|
|
2008
|
+
--organization-id <id> Create in a specific organization (defaults to membership)
|
|
2009
|
+
|
|
2010
|
+
This command is also available as \`ovld protocol create-project\`.`);
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
2015
|
+
await resolveProtocolAuthForFlags(flags);
|
|
2016
|
+
const timeoutMs = resolveTimeout(flags);
|
|
2017
|
+
|
|
2018
|
+
const name = typeof flags.name === 'string' ? flags.name.trim() : '';
|
|
2019
|
+
if (!name) {
|
|
2020
|
+
console.error('Error: --name is required');
|
|
2021
|
+
process.exit(1);
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
// A directory makes project creation a one-step process: the project is
|
|
2025
|
+
// created and the directory is registered as its primary resource together.
|
|
2026
|
+
// Pass --no-directory (or --directory=false) to create a bare project.
|
|
2027
|
+
const directoryRequested = !(flags.directory === false || flags.directory === 'false');
|
|
2028
|
+
let directoryPath = '';
|
|
2029
|
+
if (directoryRequested) {
|
|
2030
|
+
directoryPath = path.resolve(
|
|
2031
|
+
typeof flags.directory === 'string' && flags.directory.trim()
|
|
2032
|
+
? flags.directory.trim()
|
|
2033
|
+
: process.cwd()
|
|
2034
|
+
);
|
|
2035
|
+
if (!fs.existsSync(directoryPath)) {
|
|
2036
|
+
console.error(`Error: Directory does not exist: ${directoryPath}`);
|
|
2037
|
+
process.exit(1);
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
let deviceFingerprint = '';
|
|
2042
|
+
if (directoryPath) {
|
|
2043
|
+
deviceFingerprint = String(
|
|
2044
|
+
flags['device-fingerprint'] ?? process.env.OVERLORD_DEVICE_FINGERPRINT ?? ''
|
|
2045
|
+
).trim();
|
|
2046
|
+
if (!deviceFingerprint) {
|
|
2047
|
+
const { readOrCreateDeviceFingerprint } = await import('./runner.mjs');
|
|
2048
|
+
deviceFingerprint = readOrCreateDeviceFingerprint(flags);
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
const isPrimary =
|
|
2053
|
+
flags['is-primary'] !== undefined
|
|
2054
|
+
? flags['is-primary'] === true || flags['is-primary'] === 'true'
|
|
2055
|
+
: flags.primary !== undefined
|
|
2056
|
+
? !(flags.primary === false || flags.primary === 'false')
|
|
2057
|
+
: undefined;
|
|
2058
|
+
|
|
2059
|
+
const body = {
|
|
2060
|
+
name,
|
|
2061
|
+
...(typeof flags.color === 'string' && flags.color.trim() ? { color: flags.color.trim() } : {}),
|
|
2062
|
+
...(directoryPath
|
|
2063
|
+
? {
|
|
2064
|
+
directoryPath,
|
|
2065
|
+
deviceFingerprint,
|
|
2066
|
+
...(isPrimary !== undefined ? { isPrimary } : {}),
|
|
2067
|
+
...(typeof flags.label === 'string' ? { label: flags.label } : {}),
|
|
2068
|
+
...(flags['device-hostname']
|
|
2069
|
+
? { deviceHostname: String(flags['device-hostname']) }
|
|
2070
|
+
: { deviceHostname: os.hostname() }),
|
|
2071
|
+
...(flags['device-platform']
|
|
2072
|
+
? { devicePlatform: String(flags['device-platform']) }
|
|
2073
|
+
: { devicePlatform: process.platform }),
|
|
2074
|
+
...(flags['device-port'] != null ? { devicePort: Number(flags['device-port']) } : {})
|
|
2075
|
+
}
|
|
2076
|
+
: {})
|
|
2077
|
+
};
|
|
2078
|
+
|
|
2079
|
+
const data = await apiPost(
|
|
2080
|
+
platformUrl,
|
|
2081
|
+
bearerToken,
|
|
2082
|
+
localSecret,
|
|
2083
|
+
organizationId,
|
|
2084
|
+
'/api/protocol/create-project',
|
|
2085
|
+
body,
|
|
2086
|
+
timeoutMs
|
|
2087
|
+
);
|
|
2088
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2089
|
+
|
|
2090
|
+
if (data.project?.id) {
|
|
2091
|
+
process.stderr.write(`\nPROJECT_ID=${data.project.id}\n`);
|
|
2092
|
+
}
|
|
2093
|
+
if (data.resource?.id) {
|
|
2094
|
+
process.stderr.write(`RESOURCE_ID=${data.resource.id}\n`);
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
// Write .overlord/project.json so future cwd-based resolution finds the project.
|
|
2098
|
+
if (directoryPath && data.project?.id && data.project?.name) {
|
|
2099
|
+
try {
|
|
2100
|
+
const { upsertLocalOverlordConfig } = await import('./local-config.mjs');
|
|
2101
|
+
const result = await upsertLocalOverlordConfig({
|
|
2102
|
+
directoryPath,
|
|
2103
|
+
project: { id: data.project.id, name: data.project.name }
|
|
2104
|
+
});
|
|
2105
|
+
process.stderr.write(`OVERLORD_CONFIG=${result.filePath} (${result.action})\n`);
|
|
2106
|
+
} catch (configError) {
|
|
2107
|
+
process.stderr.write(
|
|
2108
|
+
`\nWarning: could not write .overlord/project.json: ${configError instanceof Error ? configError.message : configError}\n`
|
|
2109
|
+
);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
|
|
1985
2114
|
// ---------------------------------------------------------------------------
|
|
1986
2115
|
// execution requests (durable runner queue)
|
|
1987
2116
|
// ---------------------------------------------------------------------------
|
|
@@ -2750,6 +2879,7 @@ Subcommands:
|
|
|
2750
2879
|
attachment-upload-file Prepare, upload, and finalize a local file in one command
|
|
2751
2880
|
get-device Identify / register the current device and return its label
|
|
2752
2881
|
update-device Rename this device's label (must be lowercase kebab-case)
|
|
2882
|
+
create-project Create a project, optionally linking the current directory in one step
|
|
2753
2883
|
list-project-resources List resource directories registered for a project
|
|
2754
2884
|
add-project-resource Register a local directory as a project resource on this device
|
|
2755
2885
|
update-project-resource Update a resource directory's path, label, or primary status
|
|
@@ -3180,6 +3310,20 @@ update-device:
|
|
|
3180
3310
|
--device-fingerprint <fp> (or OVERLORD_DEVICE_FINGERPRINT env var)
|
|
3181
3311
|
--label <label> Lowercase kebab-case, unique within org
|
|
3182
3312
|
|
|
3313
|
+
create-project:
|
|
3314
|
+
Required:
|
|
3315
|
+
--name <text> Project name
|
|
3316
|
+
Optional:
|
|
3317
|
+
--color <#rrggbb> Project color (defaults to a soft rose)
|
|
3318
|
+
--directory <path> Register this directory as the project's primary resource
|
|
3319
|
+
in one step (defaults to the current working directory;
|
|
3320
|
+
pass --no-directory to create a bare project)
|
|
3321
|
+
--device-fingerprint <fp> Device for the directory (or OVERLORD_DEVICE_FINGERPRINT;
|
|
3322
|
+
auto-generated when omitted)
|
|
3323
|
+
--label <text> Label for the registered directory
|
|
3324
|
+
--is-primary <true|false> Override primary status (directory defaults to primary)
|
|
3325
|
+
--organization-id <id> Create in a specific organization (defaults to your membership)
|
|
3326
|
+
|
|
3183
3327
|
list-project-resources:
|
|
3184
3328
|
Required:
|
|
3185
3329
|
--project-id <uuid>
|
|
@@ -3322,6 +3466,8 @@ Examples:
|
|
|
3322
3466
|
EOF
|
|
3323
3467
|
ovld protocol get-device --device-fingerprint $OVERLORD_DEVICE_FINGERPRINT
|
|
3324
3468
|
ovld protocol update-device --device-fingerprint $OVERLORD_DEVICE_FINGERPRINT --label work-macbook
|
|
3469
|
+
ovld protocol create-project --name "Acme Web" --directory /path/to/repo
|
|
3470
|
+
ovld protocol create-project --name "Acme Web" --no-directory
|
|
3325
3471
|
ovld protocol list-project-resources --project-id <project-uuid>
|
|
3326
3472
|
ovld protocol list-project-resources --project-id <project-uuid> --device-fingerprint $OVERLORD_DEVICE_FINGERPRINT
|
|
3327
3473
|
ovld protocol add-project-resource --project-id <project-uuid> --device-fingerprint $OVERLORD_DEVICE_FINGERPRINT
|
|
@@ -3476,6 +3622,10 @@ EOF
|
|
|
3476
3622
|
await protocolUpdateDevice(args);
|
|
3477
3623
|
return;
|
|
3478
3624
|
}
|
|
3625
|
+
if (subcommand === 'create-project') {
|
|
3626
|
+
await protocolCreateProject(args);
|
|
3627
|
+
return;
|
|
3628
|
+
}
|
|
3479
3629
|
if (subcommand === 'list-project-resources') {
|
|
3480
3630
|
await protocolListProjectResources(args);
|
|
3481
3631
|
return;
|
package/bin/_cli/setup.mjs
CHANGED
|
@@ -1125,6 +1125,95 @@ function removeCursorBeforeSubmitHook(paths) {
|
|
|
1125
1125
|
}
|
|
1126
1126
|
|
|
1127
1127
|
|
|
1128
|
+
// ---------------------------------------------------------------------------
|
|
1129
|
+
// Headless marketplace plugin install (Claude + Codex, via the agent CLI)
|
|
1130
|
+
// ---------------------------------------------------------------------------
|
|
1131
|
+
|
|
1132
|
+
const OVERLORD_PLUGIN_NAME = 'overlord';
|
|
1133
|
+
const OVERLORD_MARKETPLACE_NAME = 'overlord-local';
|
|
1134
|
+
const OVERLORD_PLUGIN_KEY = `${OVERLORD_PLUGIN_NAME}@${OVERLORD_MARKETPLACE_NAME}`;
|
|
1135
|
+
const CLAUDE_MARKETPLACE_ROOT = path.join(os.homedir(), '.ovld', 'claude-marketplace');
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Run an agent CLI plugin command without throwing.
|
|
1139
|
+
* `missing` is true when the agent binary is not on PATH (ENOENT).
|
|
1140
|
+
*
|
|
1141
|
+
* @returns {{ ok: boolean, missing: boolean, stdout: string, stderr: string }}
|
|
1142
|
+
*/
|
|
1143
|
+
function runAgentPluginCli(binary, args) {
|
|
1144
|
+
try {
|
|
1145
|
+
const stdout = execFileSync(binary, args, {
|
|
1146
|
+
encoding: 'utf8',
|
|
1147
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
1148
|
+
});
|
|
1149
|
+
return { ok: true, missing: false, stdout: stdout ?? '', stderr: '' };
|
|
1150
|
+
} catch (err) {
|
|
1151
|
+
return {
|
|
1152
|
+
ok: false,
|
|
1153
|
+
missing: err?.code === 'ENOENT',
|
|
1154
|
+
stdout: err?.stdout?.toString?.() ?? '',
|
|
1155
|
+
stderr: err?.stderr?.toString?.() ?? ''
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
/**
|
|
1161
|
+
* Build the local marketplace wrapper under ~/.ovld/claude-marketplace and
|
|
1162
|
+
* register + install the Overlord plugin through the `claude` CLI so the plugin
|
|
1163
|
+
* (and its `overlord:overlord-ticket` skill) loads in every Claude session —
|
|
1164
|
+
* including headless/pod environments where Overlord Desktop never ran. Mirrors
|
|
1165
|
+
* the desktop installer's registerClaudeLocalMarketplace, but drives Claude's
|
|
1166
|
+
* own plugin system instead of hand-writing its JSON.
|
|
1167
|
+
*
|
|
1168
|
+
* @returns {{ ok: boolean, missing: boolean }}
|
|
1169
|
+
*/
|
|
1170
|
+
function installClaudeMarketplacePlugin(sourceDir, version) {
|
|
1171
|
+
const pluginDir = path.join(CLAUDE_MARKETPLACE_ROOT, 'plugins', OVERLORD_PLUGIN_NAME);
|
|
1172
|
+
fs.mkdirSync(path.dirname(pluginDir), { recursive: true });
|
|
1173
|
+
fs.rmSync(pluginDir, { recursive: true, force: true });
|
|
1174
|
+
fs.cpSync(sourceDir, pluginDir, { recursive: true });
|
|
1175
|
+
|
|
1176
|
+
const marketplaceManifest = {
|
|
1177
|
+
name: OVERLORD_MARKETPLACE_NAME,
|
|
1178
|
+
owner: { name: 'Cooperativ', email: 'support@ovld.ai' },
|
|
1179
|
+
metadata: {
|
|
1180
|
+
description: 'Overlord plugin registered locally by the Overlord CLI.',
|
|
1181
|
+
version
|
|
1182
|
+
},
|
|
1183
|
+
plugins: [
|
|
1184
|
+
{
|
|
1185
|
+
name: OVERLORD_PLUGIN_NAME,
|
|
1186
|
+
source: `./plugins/${OVERLORD_PLUGIN_NAME}`,
|
|
1187
|
+
description:
|
|
1188
|
+
'Overlord ticket protocol workflow for Claude Code (attach/update/ask/deliver, slash commands, permission hook).',
|
|
1189
|
+
version
|
|
1190
|
+
}
|
|
1191
|
+
]
|
|
1192
|
+
};
|
|
1193
|
+
const manifestPath = path.join(CLAUDE_MARKETPLACE_ROOT, '.claude-plugin', 'marketplace.json');
|
|
1194
|
+
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
1195
|
+
fs.writeFileSync(manifestPath, JSON.stringify(marketplaceManifest, null, 2) + '\n');
|
|
1196
|
+
|
|
1197
|
+
// Register the marketplace (idempotent: refresh if it was already added).
|
|
1198
|
+
const added = runAgentPluginCli('claude', [
|
|
1199
|
+
'plugin',
|
|
1200
|
+
'marketplace',
|
|
1201
|
+
'add',
|
|
1202
|
+
CLAUDE_MARKETPLACE_ROOT
|
|
1203
|
+
]);
|
|
1204
|
+
if (added.missing) return { ok: false, missing: true };
|
|
1205
|
+
if (!added.ok) {
|
|
1206
|
+
runAgentPluginCli('claude', ['plugin', 'marketplace', 'update', OVERLORD_MARKETPLACE_NAME]);
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// Install the plugin (idempotent: update if it was already installed).
|
|
1210
|
+
let installed = runAgentPluginCli('claude', ['plugin', 'install', OVERLORD_PLUGIN_KEY]);
|
|
1211
|
+
if (!installed.ok && !installed.missing) {
|
|
1212
|
+
installed = runAgentPluginCli('claude', ['plugin', 'update', OVERLORD_PLUGIN_KEY]);
|
|
1213
|
+
}
|
|
1214
|
+
return { ok: installed.ok, missing: installed.missing };
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1128
1217
|
function installClaude() {
|
|
1129
1218
|
const sourceDir = claudeSourcePluginDir();
|
|
1130
1219
|
const sourceManifest = path.join(sourceDir, '.claude-plugin', 'plugin.json');
|
|
@@ -1138,9 +1227,18 @@ function installClaude() {
|
|
|
1138
1227
|
} else {
|
|
1139
1228
|
console.log(' ✓ No v3.25 Claude connector files needed migration.');
|
|
1140
1229
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
)
|
|
1230
|
+
|
|
1231
|
+
const result = installClaudeMarketplacePlugin(sourceDir, sourceVersion);
|
|
1232
|
+
if (result.ok) {
|
|
1233
|
+
console.log(` ✓ Registered + installed the Claude plugin (${OVERLORD_PLUGIN_KEY}) via the claude CLI.`);
|
|
1234
|
+
} else if (result.missing) {
|
|
1235
|
+
console.log(' ⚠ `claude` CLI not found on PATH — wrote the local marketplace but could not install the plugin.');
|
|
1236
|
+
console.log(
|
|
1237
|
+
` Run \`claude plugin marketplace add ${CLAUDE_MARKETPLACE_ROOT} && claude plugin install ${OVERLORD_PLUGIN_KEY}\` once Claude Code is installed.`
|
|
1238
|
+
);
|
|
1239
|
+
} else {
|
|
1240
|
+
console.log(' ⚠ Could not install the Claude plugin via the claude CLI. The local marketplace was written; rerun `ovld setup claude` to retry.');
|
|
1241
|
+
}
|
|
1144
1242
|
|
|
1145
1243
|
const manifest = readManifest();
|
|
1146
1244
|
manifest.claude = {
|
|
@@ -1234,6 +1332,21 @@ function installCodex() {
|
|
|
1234
1332
|
|
|
1235
1333
|
removeLegacyCodexBundle();
|
|
1236
1334
|
|
|
1335
|
+
// Copying the plugin + writing the marketplace only makes the plugin
|
|
1336
|
+
// "available". `codex plugin add` is what actually installs/enables it, so the
|
|
1337
|
+
// skill loads in headless/pod sessions where Overlord Desktop never ran.
|
|
1338
|
+
const codexAdd = runAgentPluginCli('codex', ['plugin', 'add', OVERLORD_PLUGIN_KEY]);
|
|
1339
|
+
if (codexAdd.ok) {
|
|
1340
|
+
console.log(` ✓ Installed plugin via codex CLI: ${OVERLORD_PLUGIN_KEY}`);
|
|
1341
|
+
} else if (codexAdd.missing) {
|
|
1342
|
+
console.log(' ⚠ `codex` CLI not found on PATH — wrote the marketplace but could not install the plugin.');
|
|
1343
|
+
console.log(` Run \`codex plugin add ${OVERLORD_PLUGIN_KEY}\` once Codex is installed.`);
|
|
1344
|
+
} else if (/already/i.test(`${codexAdd.stdout}${codexAdd.stderr}`)) {
|
|
1345
|
+
console.log(` ✓ Codex plugin already installed: ${OVERLORD_PLUGIN_KEY}`);
|
|
1346
|
+
} else {
|
|
1347
|
+
console.log(' ⚠ Could not install the Codex plugin via the codex CLI. The marketplace was written; rerun `ovld setup codex` to retry.');
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1237
1350
|
const installedFiles = [
|
|
1238
1351
|
...listFilesRecursive(CODEX_TARGET_PLUGIN_DIR),
|
|
1239
1352
|
CODEX_TARGET_MARKETPLACE,
|
|
@@ -1832,7 +1945,10 @@ function installCodexPermissions() {
|
|
|
1832
1945
|
// ---------------------------------------------------------------------------
|
|
1833
1946
|
|
|
1834
1947
|
export async function runSetupCommand(args) {
|
|
1835
|
-
const
|
|
1948
|
+
const yesFlag = args.includes('--yes') || args.includes('-y') ||
|
|
1949
|
+
/^(1|true|yes|on)$/i.test(String(process.env.AGENT_POD_OVERLORD ?? '').trim());
|
|
1950
|
+
const filteredArgs = args.filter(a => a !== '--yes' && a !== '-y');
|
|
1951
|
+
const agent = filteredArgs[0];
|
|
1836
1952
|
|
|
1837
1953
|
if (agent === '--help' || agent === '-h' || agent === 'help') {
|
|
1838
1954
|
console.log(`Usage:
|
|
@@ -1843,7 +1959,10 @@ export async function runSetupCommand(args) {
|
|
|
1843
1959
|
ovld setup antigravity Install Overlord Antigravity plugin (agy) and protocol policy rules
|
|
1844
1960
|
ovld setup opencode Install Overlord connector for OpenCode
|
|
1845
1961
|
ovld setup all Prepare all supported agents
|
|
1846
|
-
ovld doctor Validate installed connectors and check for CLI updates
|
|
1962
|
+
ovld doctor Validate installed connectors and check for CLI updates
|
|
1963
|
+
|
|
1964
|
+
Options:
|
|
1965
|
+
--yes, -y Auto-approve all prompts (for non-interactive / pod environments)`);
|
|
1847
1966
|
return;
|
|
1848
1967
|
}
|
|
1849
1968
|
|
|
@@ -1912,7 +2031,7 @@ export async function runSetupCommand(args) {
|
|
|
1912
2031
|
console.log('Agent plugins/connectors prepared successfully!\n');
|
|
1913
2032
|
|
|
1914
2033
|
printAgentPermissionsDescription();
|
|
1915
|
-
const shouldInstallPermissions = await askYesNo(
|
|
2034
|
+
const shouldInstallPermissions = yesFlag || await askYesNo(
|
|
1916
2035
|
'Would you like to configure agent permissions for Overlord protocol access?',
|
|
1917
2036
|
true
|
|
1918
2037
|
);
|
|
@@ -1959,7 +2078,7 @@ export async function runSetupCommand(args) {
|
|
|
1959
2078
|
if (agentsThatNeedPermissions.length > 0) {
|
|
1960
2079
|
console.log();
|
|
1961
2080
|
printAgentPermissionsDescription();
|
|
1962
|
-
const shouldInstallPermissions = await askYesNo(
|
|
2081
|
+
const shouldInstallPermissions = yesFlag || await askYesNo(
|
|
1963
2082
|
'Would you like to configure agent permissions for Overlord protocol access?',
|
|
1964
2083
|
true
|
|
1965
2084
|
);
|
|
@@ -1994,7 +2113,7 @@ export async function runSetupCommand(args) {
|
|
|
1994
2113
|
if (['claude', 'codex', 'opencode'].includes(agent)) {
|
|
1995
2114
|
console.log();
|
|
1996
2115
|
printAgentPermissionsDescription();
|
|
1997
|
-
const shouldInstallPermissions = await askYesNo(
|
|
2116
|
+
const shouldInstallPermissions = yesFlag || await askYesNo(
|
|
1998
2117
|
'Would you like to configure agent permissions for Overlord protocol access?',
|
|
1999
2118
|
true
|
|
2000
2119
|
);
|
package/package.json
CHANGED
|
@@ -114,5 +114,5 @@ For the `record-change-rationales` command and full payload shape with optional
|
|
|
114
114
|
- [reference/context.md](reference/context.md) — Shared state, attachments, and large artifact policy
|
|
115
115
|
- [reference/shell-escaping.md](reference/shell-escaping.md) — Heredoc stdin piping for special characters in summaries and payloads
|
|
116
116
|
|
|
117
|
-
<!-- version: 0.5.
|
|
117
|
+
<!-- version: 0.5.12 -->
|
|
118
118
|
|
|
@@ -140,6 +140,29 @@ ovld protocol discover-project --working-directory /path/to/repo
|
|
|
140
140
|
|
|
141
141
|
Use `--project-id` when the project id is already known. Use `--working-directory` to override cwd path matching. If the runtime has an `OVERLORD_DEVICE_FINGERPRINT`, pass `--device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"` so resource-directory matching prefers the current device.
|
|
142
142
|
|
|
143
|
+
## Creating Projects
|
|
144
|
+
|
|
145
|
+
Users and agents can create a project directly from the CLI. By default the current
|
|
146
|
+
working directory is registered as the new project's primary resource in the same
|
|
147
|
+
call (one-step setup); pass `--no-directory` to create a bare project.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Create a project and link the current directory in one step
|
|
151
|
+
ovld protocol create-project --name "Acme Web"
|
|
152
|
+
|
|
153
|
+
# Link a specific directory
|
|
154
|
+
ovld protocol create-project --name "Acme Web" --directory /path/to/repo
|
|
155
|
+
|
|
156
|
+
# Create a bare project with no directory
|
|
157
|
+
ovld protocol create-project --name "Acme Web" --no-directory
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`ovld create-project` is a friendly top-level alias for `ovld protocol create-project`.
|
|
161
|
+
When a directory is registered the command also writes `.overlord/project.json` so
|
|
162
|
+
future cwd-based resolution finds the project. Pass `--organization-id <id>` to create
|
|
163
|
+
in a specific organization (defaults to your membership); `--color <#rrggbb>` sets the
|
|
164
|
+
project color.
|
|
165
|
+
|
|
143
166
|
### Resolving the project ID when you don't have one
|
|
144
167
|
|
|
145
168
|
When you need a project ID for a protocol command and the ticket prompt did not supply one, resolve it in this order.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Execution Targets And Checkout Paths
|
|
2
2
|
|
|
3
|
-
Execution targets are canonical rows keyed by a real `device_fingerprint` when known, or by a placeholder until the target registers. Organization labels, user access, project membership, and project checkout paths live in separate association rows. Persist a fingerprint per machine, upsert via `ovld protocol get-device`, then maintain checkout paths via `list-project-resources`, `add-project-resource`, `update-project-resource`, and `update-device` (`ovld protocol help` lists flags).
|
|
3
|
+
Execution targets are canonical rows keyed by a real `device_fingerprint` when known, or by a placeholder until the target registers. Organization labels, user access, project membership, and project checkout paths live in separate association rows. Persist a fingerprint per machine, upsert via `ovld protocol get-device`, then maintain checkout paths via `list-project-resources`, `add-project-resource`, `update-project-resource`, and `update-device` (`ovld protocol help` lists flags). To create a brand-new project (optionally registering a directory as its primary resource in one step) use `ovld protocol create-project --name "..."`.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
ovld protocol get-device --device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"
|
|
@@ -114,5 +114,5 @@ For the `record-change-rationales` command and full payload shape with optional
|
|
|
114
114
|
- [reference/context.md](reference/context.md) — Shared state, attachments, and large artifact policy
|
|
115
115
|
- [reference/shell-escaping.md](reference/shell-escaping.md) — Heredoc stdin piping for special characters in summaries and payloads
|
|
116
116
|
|
|
117
|
-
<!-- version: 0.5.
|
|
117
|
+
<!-- version: 0.5.12 -->
|
|
118
118
|
|
|
@@ -140,6 +140,29 @@ ovld protocol discover-project --working-directory /path/to/repo
|
|
|
140
140
|
|
|
141
141
|
Use `--project-id` when the project id is already known. Use `--working-directory` to override cwd path matching. If the runtime has an `OVERLORD_DEVICE_FINGERPRINT`, pass `--device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"` so resource-directory matching prefers the current device.
|
|
142
142
|
|
|
143
|
+
## Creating Projects
|
|
144
|
+
|
|
145
|
+
Users and agents can create a project directly from the CLI. By default the current
|
|
146
|
+
working directory is registered as the new project's primary resource in the same
|
|
147
|
+
call (one-step setup); pass `--no-directory` to create a bare project.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Create a project and link the current directory in one step
|
|
151
|
+
ovld protocol create-project --name "Acme Web"
|
|
152
|
+
|
|
153
|
+
# Link a specific directory
|
|
154
|
+
ovld protocol create-project --name "Acme Web" --directory /path/to/repo
|
|
155
|
+
|
|
156
|
+
# Create a bare project with no directory
|
|
157
|
+
ovld protocol create-project --name "Acme Web" --no-directory
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`ovld create-project` is a friendly top-level alias for `ovld protocol create-project`.
|
|
161
|
+
When a directory is registered the command also writes `.overlord/project.json` so
|
|
162
|
+
future cwd-based resolution finds the project. Pass `--organization-id <id>` to create
|
|
163
|
+
in a specific organization (defaults to your membership); `--color <#rrggbb>` sets the
|
|
164
|
+
project color.
|
|
165
|
+
|
|
143
166
|
### Resolving the project ID when you don't have one
|
|
144
167
|
|
|
145
168
|
When you need a project ID for a protocol command and the ticket prompt did not supply one, resolve it in this order.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Execution Targets And Checkout Paths
|
|
2
2
|
|
|
3
|
-
Execution targets are canonical rows keyed by a real `device_fingerprint` when known, or by a placeholder until the target registers. Organization labels, user access, project membership, and project checkout paths live in separate association rows. Persist a fingerprint per machine, upsert via `ovld protocol get-device`, then maintain checkout paths via `list-project-resources`, `add-project-resource`, `update-project-resource`, and `update-device` (`ovld protocol help` lists flags).
|
|
3
|
+
Execution targets are canonical rows keyed by a real `device_fingerprint` when known, or by a placeholder until the target registers. Organization labels, user access, project membership, and project checkout paths live in separate association rows. Persist a fingerprint per machine, upsert via `ovld protocol get-device`, then maintain checkout paths via `list-project-resources`, `add-project-resource`, `update-project-resource`, and `update-device` (`ovld protocol help` lists flags). To create a brand-new project (optionally registering a directory as its primary resource in one step) use `ovld protocol create-project --name "..."`.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
ovld protocol get-device --device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"
|
|
@@ -856,6 +856,61 @@ const tools = [
|
|
|
856
856
|
}),
|
|
857
857
|
subcommand: 'update-device'
|
|
858
858
|
},
|
|
859
|
+
{
|
|
860
|
+
name: 'create_project',
|
|
861
|
+
description:
|
|
862
|
+
'Create a new project. By default the current working directory is registered as the project\'s primary resource in one step; pass no_directory:true for a bare project, or directory_path to link a specific directory.',
|
|
863
|
+
inputSchema: {
|
|
864
|
+
type: 'object',
|
|
865
|
+
properties: {
|
|
866
|
+
name: { type: 'string', description: 'Project name.' },
|
|
867
|
+
color: { type: 'string', description: 'Optional hex color like #d4d4d8.' },
|
|
868
|
+
directory_path: {
|
|
869
|
+
type: 'string',
|
|
870
|
+
description: 'Absolute path to register as primary; omit to use cwd.'
|
|
871
|
+
},
|
|
872
|
+
no_directory: {
|
|
873
|
+
type: 'boolean',
|
|
874
|
+
description: 'Create a bare project without registering any directory.'
|
|
875
|
+
},
|
|
876
|
+
device_fingerprint: { type: 'string' },
|
|
877
|
+
label: { type: 'string' },
|
|
878
|
+
is_primary: { type: 'boolean' },
|
|
879
|
+
device_hostname: { type: 'string' },
|
|
880
|
+
device_platform: { type: 'string' },
|
|
881
|
+
organization_id: { type: 'string' }
|
|
882
|
+
},
|
|
883
|
+
required: ['name']
|
|
884
|
+
},
|
|
885
|
+
toCliFlags: args => ({
|
|
886
|
+
name: args.name,
|
|
887
|
+
...(typeof args.color === 'string' && args.color.trim().length > 0
|
|
888
|
+
? { color: args.color.trim() }
|
|
889
|
+
: {}),
|
|
890
|
+
...(args.no_directory === true || args.no_directory === 'true'
|
|
891
|
+
? { directory: 'false' }
|
|
892
|
+
: typeof args.directory_path === 'string' && args.directory_path.trim().length > 0
|
|
893
|
+
? { directory: args.directory_path }
|
|
894
|
+
: {}),
|
|
895
|
+
...(typeof args.device_fingerprint === 'string' && args.device_fingerprint.trim().length > 0
|
|
896
|
+
? { 'device-fingerprint': args.device_fingerprint.trim() }
|
|
897
|
+
: {}),
|
|
898
|
+
...(typeof args.label === 'string' ? { label: args.label } : {}),
|
|
899
|
+
...(args.is_primary !== undefined
|
|
900
|
+
? { 'is-primary': args.is_primary === true || args.is_primary === 'true' }
|
|
901
|
+
: {}),
|
|
902
|
+
...(typeof args.device_hostname === 'string' && args.device_hostname.trim().length > 0
|
|
903
|
+
? { 'device-hostname': args.device_hostname }
|
|
904
|
+
: {}),
|
|
905
|
+
...(typeof args.device_platform === 'string' && args.device_platform.trim().length > 0
|
|
906
|
+
? { 'device-platform': args.device_platform }
|
|
907
|
+
: {}),
|
|
908
|
+
...(typeof args.organization_id === 'string' && args.organization_id.trim().length > 0
|
|
909
|
+
? { 'organization-id': args.organization_id.trim() }
|
|
910
|
+
: {})
|
|
911
|
+
}),
|
|
912
|
+
subcommand: 'create-project'
|
|
913
|
+
},
|
|
859
914
|
{
|
|
860
915
|
name: 'list_project_resources',
|
|
861
916
|
description:
|
|
@@ -115,5 +115,5 @@ For the `record-change-rationales` command and full payload shape with optional
|
|
|
115
115
|
- [reference/context.md](reference/context.md) — Shared state, attachments, and large artifact policy
|
|
116
116
|
- [reference/shell-escaping.md](reference/shell-escaping.md) — Heredoc stdin piping for special characters in summaries and payloads
|
|
117
117
|
|
|
118
|
-
<!-- version: 0.5.
|
|
118
|
+
<!-- version: 0.5.12 -->
|
|
119
119
|
|
|
@@ -140,6 +140,29 @@ ovld protocol discover-project --working-directory /path/to/repo
|
|
|
140
140
|
|
|
141
141
|
Use `--project-id` when the project id is already known. Use `--working-directory` to override cwd path matching. If the runtime has an `OVERLORD_DEVICE_FINGERPRINT`, pass `--device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"` so resource-directory matching prefers the current device.
|
|
142
142
|
|
|
143
|
+
## Creating Projects
|
|
144
|
+
|
|
145
|
+
Users and agents can create a project directly from the CLI. By default the current
|
|
146
|
+
working directory is registered as the new project's primary resource in the same
|
|
147
|
+
call (one-step setup); pass `--no-directory` to create a bare project.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Create a project and link the current directory in one step
|
|
151
|
+
ovld protocol create-project --name "Acme Web"
|
|
152
|
+
|
|
153
|
+
# Link a specific directory
|
|
154
|
+
ovld protocol create-project --name "Acme Web" --directory /path/to/repo
|
|
155
|
+
|
|
156
|
+
# Create a bare project with no directory
|
|
157
|
+
ovld protocol create-project --name "Acme Web" --no-directory
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`ovld create-project` is a friendly top-level alias for `ovld protocol create-project`.
|
|
161
|
+
When a directory is registered the command also writes `.overlord/project.json` so
|
|
162
|
+
future cwd-based resolution finds the project. Pass `--organization-id <id>` to create
|
|
163
|
+
in a specific organization (defaults to your membership); `--color <#rrggbb>` sets the
|
|
164
|
+
project color.
|
|
165
|
+
|
|
143
166
|
### Resolving the project ID when you don't have one
|
|
144
167
|
|
|
145
168
|
When you need a project ID for a protocol command and the ticket prompt did not supply one, resolve it in this order.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Execution Targets And Checkout Paths
|
|
2
2
|
|
|
3
|
-
Execution targets are canonical rows keyed by a real `device_fingerprint` when known, or by a placeholder until the target registers. Organization labels, user access, project membership, and project checkout paths live in separate association rows. Persist a fingerprint per machine, upsert via `ovld protocol get-device`, then maintain checkout paths via `list-project-resources`, `add-project-resource`, `update-project-resource`, and `update-device` (`ovld protocol help` lists flags).
|
|
3
|
+
Execution targets are canonical rows keyed by a real `device_fingerprint` when known, or by a placeholder until the target registers. Organization labels, user access, project membership, and project checkout paths live in separate association rows. Persist a fingerprint per machine, upsert via `ovld protocol get-device`, then maintain checkout paths via `list-project-resources`, `add-project-resource`, `update-project-resource`, and `update-device` (`ovld protocol help` lists flags). To create a brand-new project (optionally registering a directory as its primary resource in one step) use `ovld protocol create-project --name "..."`.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
ovld protocol get-device --device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"
|