@joenandez/academy 0.5.17 → 0.5.18
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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/scripts/cli/args-kinds.mjs +25 -12
- package/scripts/cli/args.mjs +4 -2
- package/scripts/cli/create-options.mjs +43 -0
- package/scripts/cli/create.mjs +7 -39
- package/scripts/cli/scaffold.mjs +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
4
4
|
The heading at the top of this file is the version in `package.json`.
|
|
5
5
|
|
|
6
|
+
## 0.5.18 — 2026-11-11
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **`academy create --runtime claude-code|codex|copilot`.** `create` stores
|
|
11
|
+
the agent's launch runtime, the same value `run --agent` persists, before
|
|
12
|
+
its jobs register. Before, `create` refused the flag, so a client that hired
|
|
13
|
+
a Codex agent (Subspace's Hire a Principal) failed. An unknown value is
|
|
14
|
+
refused with `invalid_spec` and writes nothing.
|
|
15
|
+
|
|
6
16
|
## 0.5.17 — 2026-11-11
|
|
7
17
|
|
|
8
18
|
This release collects the 0.5.17 release candidates (rc.1 to rc.6, below) and
|
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ academy tokens <name> [--json] # compiled prompt size by surface
|
|
|
80
80
|
academy budget <name> [--json] # surfaces over their cap
|
|
81
81
|
academy sessions [--agent <name>] [--json]
|
|
82
82
|
academy events --since <seq> [--logid <id>] [--json]
|
|
83
|
-
academy create [<name>] [--kind <kind>] [--model <id>] [--display-name <text>] [--json]
|
|
83
|
+
academy create [<name>] [--kind <kind>] [--model <id>] [--display-name <text>] [--runtime claude-code|codex|copilot] [--no-start] [--json]
|
|
84
84
|
academy hire # interactive
|
|
85
85
|
academy hire --spec <file> [--json] # headless
|
|
86
86
|
academy rename <old> [<new>] [--display-name <text>] [--json] # no folder or job changes
|
package/package.json
CHANGED
|
@@ -1,29 +1,42 @@
|
|
|
1
|
-
// `create <name> [--kind <kind>] [--model <id>] [--display-name <text>]
|
|
2
|
-
// [--json]`. Like
|
|
1
|
+
// `create <name> [--kind <kind>] [--model <id>] [--display-name <text>]
|
|
2
|
+
// [--runtime <runtime>] [--no-start] [--json]`. Like
|
|
3
3
|
// `rename`, an option Academy does not know stays on the command so the handler
|
|
4
4
|
// answers it inside the envelope the caller asked for. The kind and model stay
|
|
5
5
|
// raw strings: only `create` can answer an unsupported one in the envelope.
|
|
6
|
+
const CREATE_VALUE_FLAGS = {
|
|
7
|
+
'--kind': 'kind',
|
|
8
|
+
'--model': 'model',
|
|
9
|
+
'--display-name': 'displayName',
|
|
10
|
+
'--runtime': 'runtime',
|
|
11
|
+
};
|
|
12
|
+
|
|
6
13
|
export function parseCreateArgs(command, rest) {
|
|
7
14
|
const json = rest.includes('--json');
|
|
15
|
+
const values = {};
|
|
8
16
|
let name;
|
|
9
|
-
let kind;
|
|
10
|
-
let model;
|
|
11
|
-
let displayName;
|
|
12
17
|
let noStart = false;
|
|
13
18
|
for (let i = 0; i < rest.length; i++) {
|
|
14
19
|
const arg = rest[i];
|
|
20
|
+
const value = createValueFlag(rest, i);
|
|
15
21
|
if (arg === '--json') continue;
|
|
16
22
|
else if (arg === '--no-start') noStart = true;
|
|
17
|
-
else if (
|
|
18
|
-
else if (arg.startsWith('--kind=')) kind = arg.slice('--kind='.length);
|
|
19
|
-
else if (arg === '--model') model = rest[++i] ?? '';
|
|
20
|
-
else if (arg.startsWith('--model=')) model = arg.slice('--model='.length);
|
|
21
|
-
else if (arg === '--display-name') displayName = rest[++i] ?? '';
|
|
22
|
-
else if (arg.startsWith('--display-name=')) displayName = arg.slice('--display-name='.length);
|
|
23
|
+
else if (value) [values[value.key], i] = [value.value, value.next];
|
|
23
24
|
else if (arg.startsWith('--')) return { command, json, name, invalidOption: arg };
|
|
24
25
|
else name ??= arg;
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
+
const { kind, model, displayName, runtime } = values;
|
|
28
|
+
return { command, json, name, kind, model, displayName, runtime, noStart };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// `--flag value` or `--flag=value` for a create option that takes a value.
|
|
32
|
+
function createValueFlag(rest, i) {
|
|
33
|
+
const arg = rest[i];
|
|
34
|
+
const eq = arg.indexOf('=');
|
|
35
|
+
const flag = arg.startsWith('--') && eq > 0 ? arg.slice(0, eq) : arg;
|
|
36
|
+
const key = CREATE_VALUE_FLAGS[flag];
|
|
37
|
+
if (!key) return null;
|
|
38
|
+
if (flag !== arg) return { key, value: arg.slice(eq + 1), next: i };
|
|
39
|
+
return { key, value: rest[i + 1] ?? '', next: i + 1 };
|
|
27
40
|
}
|
|
28
41
|
|
|
29
42
|
// `skills check <agent> [--json]`. Like `create`, an option Academy does not
|
package/scripts/cli/args.mjs
CHANGED
|
@@ -175,11 +175,13 @@ Usage: ${CLI_NAME} <command> [options]
|
|
|
175
175
|
Commands:
|
|
176
176
|
doctor [--json] Report Academy's version, roots, runtimes, and capability
|
|
177
177
|
create [<name>] [--kind specialist|principal|owner] [--model <id>]
|
|
178
|
-
[--display-name <text>] [--
|
|
178
|
+
[--display-name <text>] [--runtime claude-code|codex|copilot]
|
|
179
|
+
[--no-start] [--json]
|
|
179
180
|
Scaffold a portable agent at ~/.academy/agents/<name>/ (default
|
|
180
181
|
specialist); a terminal Principal opens its first session;
|
|
181
182
|
--model stores its default model; --display-name stores a
|
|
182
|
-
person-facing name ("Principal Two"); <name> stays kebab-case
|
|
183
|
+
person-facing name ("Principal Two"); <name> stays kebab-case;
|
|
184
|
+
--runtime stores the launch runtime, as run --agent does
|
|
183
185
|
set-model <name> <model> [--json]
|
|
184
186
|
Change an agent's default model; run passes it to the
|
|
185
187
|
runtime (Claude --model, Codex -m) unless the caller does
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { KIND_PROFILES, exitJsonError } from './core.mjs';
|
|
2
|
+
import { isModelId } from './model.mjs';
|
|
3
|
+
import { toRuntimeProvider } from './runtime.mjs';
|
|
4
|
+
import { DISPLAY_NAME_RULE, isDisplayName } from './yaml.mjs';
|
|
5
|
+
|
|
6
|
+
// Every refusal comes before the name is asked about, so a create Academy will
|
|
7
|
+
// not perform writes nothing. `invalid_spec` is the published code for an
|
|
8
|
+
// unknown option; there is no sixteenth code for it.
|
|
9
|
+
export function assertCreateOptions(kind, invalidOption, json, { model, displayName, runtime }) {
|
|
10
|
+
if (invalidOption !== undefined) {
|
|
11
|
+
refuseCreate(
|
|
12
|
+
`Unknown create option: ${invalidOption}. Use create <name> [--kind <kind>] [--model <id>] [--display-name <text>] [--runtime claude-code|codex|copilot] [--no-start] [--json].`,
|
|
13
|
+
{ option: invalidOption },
|
|
14
|
+
json,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
if (model !== undefined && !isModelId(model)) {
|
|
18
|
+
refuseCreate(`Model "${model}" is not a model id Academy can store.`, { model }, json);
|
|
19
|
+
}
|
|
20
|
+
if (runtime !== undefined && !toRuntimeProvider(runtime)) {
|
|
21
|
+
refuseCreate(
|
|
22
|
+
`Runtime "${runtime}" is not a runtime Academy supports. Use claude-code, codex, or copilot.`,
|
|
23
|
+
{ runtime },
|
|
24
|
+
json,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
if (displayName !== undefined && !isDisplayName(displayName)) {
|
|
28
|
+
refuseCreate(DISPLAY_NAME_RULE, { displayName }, json);
|
|
29
|
+
}
|
|
30
|
+
if (Object.hasOwn(KIND_PROFILES, kind)) return;
|
|
31
|
+
const kinds = Object.keys(KIND_PROFILES).join(', ');
|
|
32
|
+
refuseCreate(
|
|
33
|
+
`Agent kind "${kind}" is not a kind Academy supports. Use ${kinds}.`,
|
|
34
|
+
{ kind },
|
|
35
|
+
json,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function refuseCreate(message, fields, json) {
|
|
40
|
+
if (json) exitJsonError('invalid_spec', message, fields);
|
|
41
|
+
console.error(`Error: ${message}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
package/scripts/cli/create.mjs
CHANGED
|
@@ -5,13 +5,11 @@ import { pendingMarkerPath } from '../../hooks/memory_store.mjs';
|
|
|
5
5
|
import {
|
|
6
6
|
ACADEMY_ROOT,
|
|
7
7
|
CLI_NAME,
|
|
8
|
-
KIND_PROFILES,
|
|
9
8
|
activeCommandName,
|
|
10
9
|
NIGHTLY_JOB_CRON,
|
|
11
10
|
agentDir,
|
|
12
11
|
contractError,
|
|
13
12
|
contractOk,
|
|
14
|
-
exitJsonError,
|
|
15
13
|
kindOf,
|
|
16
14
|
LockTimeoutError,
|
|
17
15
|
RuntimeUnavailableError,
|
|
@@ -26,8 +24,8 @@ import { claimDefaultName, claimName, isAgentNameFree } from './default-name.mjs
|
|
|
26
24
|
import { LogCorruptError, appendLifecycleEvent } from './eventlog.mjs';
|
|
27
25
|
import { agentRecord } from './inspect.mjs';
|
|
28
26
|
import { creatorOf } from './leads.mjs';
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
27
|
+
import { assertCreateOptions } from './create-options.mjs';
|
|
28
|
+
import { toRuntimeProvider } from './runtime.mjs';
|
|
31
29
|
import { nextSteps } from './next-steps.mjs';
|
|
32
30
|
import { deleteAcademyJobs } from './lifecycle.mjs';
|
|
33
31
|
import {
|
|
@@ -65,46 +63,16 @@ export const INTRO_PENDING_MARKER = 'intro-pending.md';
|
|
|
65
63
|
export function createAgent(
|
|
66
64
|
name,
|
|
67
65
|
json = false,
|
|
68
|
-
{ kind = DEFAULT_KIND, model, displayName, invalidOption, guided } = {},
|
|
66
|
+
{ kind = DEFAULT_KIND, model, displayName, runtime, invalidOption, guided } = {},
|
|
69
67
|
) {
|
|
70
|
-
assertCreateOptions(kind, invalidOption, json, { model, displayName });
|
|
71
|
-
|
|
68
|
+
assertCreateOptions(kind, invalidOption, json, { model, displayName, runtime });
|
|
69
|
+
// Stored in the provider form `run --agent` writes, before the jobs register.
|
|
70
|
+
const yamlOptions = { model, displayName, runtime: runtime && toRuntimeProvider(runtime) };
|
|
71
|
+
const created = provisionAgent(name, json, kind, yamlOptions);
|
|
72
72
|
reportCreated(created.name, created.dir, created.jobs, json, guided);
|
|
73
73
|
return created.name;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
// Every refusal comes before the name is asked about, so a create Academy will
|
|
77
|
-
// not perform writes nothing. `invalid_spec` is the published code for an
|
|
78
|
-
// unknown option; there is no sixteenth code for it.
|
|
79
|
-
function assertCreateOptions(kind, invalidOption, json, { model, displayName }) {
|
|
80
|
-
if (invalidOption !== undefined) {
|
|
81
|
-
refuseCreate(
|
|
82
|
-
`Unknown create option: ${invalidOption}. Use create <name> [--kind <kind>] [--model <id>] [--display-name <text>] [--no-start] [--json].`,
|
|
83
|
-
{ option: invalidOption },
|
|
84
|
-
json,
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
if (model !== undefined && !isModelId(model)) {
|
|
88
|
-
refuseCreate(`Model "${model}" is not a model id Academy can store.`, { model }, json);
|
|
89
|
-
}
|
|
90
|
-
if (displayName !== undefined && !isDisplayName(displayName)) {
|
|
91
|
-
refuseCreate(DISPLAY_NAME_RULE, { displayName }, json);
|
|
92
|
-
}
|
|
93
|
-
if (Object.hasOwn(KIND_PROFILES, kind)) return;
|
|
94
|
-
const kinds = Object.keys(KIND_PROFILES).join(', ');
|
|
95
|
-
refuseCreate(
|
|
96
|
-
`Agent kind "${kind}" is not a kind Academy supports. Use ${kinds}.`,
|
|
97
|
-
{ kind },
|
|
98
|
-
json,
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function refuseCreate(message, fields, json) {
|
|
103
|
-
if (json) exitJsonError('invalid_spec', message, fields);
|
|
104
|
-
console.error(`Error: ${message}`);
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
76
|
// The one creation path. `create` reports what it returns as a create and the
|
|
109
77
|
// headless hire reports the same thing as a hire, so an agent hired without a
|
|
110
78
|
// terminal is indistinguishable from a created one in ownership, scheduling and
|
package/scripts/cli/scaffold.mjs
CHANGED
|
@@ -62,7 +62,7 @@ function bootTemplate(kind, surface) {
|
|
|
62
62
|
// `displayName`, `model` and `createdBy` are written only when known, so an agent without them
|
|
63
63
|
// reads as null, the same as an agent created before either existed.
|
|
64
64
|
export function writeAgentYaml(dir, name, today, kind = 'specialist', options = {}) {
|
|
65
|
-
const { displayName, model, createdBy } = options;
|
|
65
|
+
const { displayName, model, createdBy, runtime = 'claude_code' } = options;
|
|
66
66
|
const { surfaces } = KIND_PROFILES[kind];
|
|
67
67
|
const totalCap = surfaces.reduce((sum, { cap }) => sum + cap, 0).toLocaleString('en-US');
|
|
68
68
|
const surfaceList = surfaces.map(({ name: surface }) => ` - ${surface}.md`).join('\n');
|
|
@@ -70,7 +70,7 @@ export function writeAgentYaml(dir, name, today, kind = 'specialist', options =
|
|
|
70
70
|
name: ${name}
|
|
71
71
|
${displayName ? `displayName: ${displayNameScalar(displayName)}\n` : ''}created: ${today}
|
|
72
72
|
${createdBy ? `created_by: ${createdBy}\n` : ''}# Launch runtime — claude_code, codex, or copilot. \`academy run --agent\` rewrites it.
|
|
73
|
-
runtime:
|
|
73
|
+
runtime: ${runtime}
|
|
74
74
|
${model ? `model: ${model}\n` : ''}# Launch permissions. yolo skips approvals and the sandbox; delete it for the runtime's default.
|
|
75
75
|
permission_mode: yolo
|
|
76
76
|
# Memory systems that are on — list them, e.g. "memory: [backstory]". Leave it out for the default.
|