@agentworkforce/cli 2.1.4 → 3.0.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/CHANGELOG.md +14 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +186 -236
- package/dist/cli.js.map +1 -1
- package/dist/cli.test.js +60 -79
- package/dist/cli.test.js.map +1 -1
- package/dist/deploy-command.d.ts +10 -0
- package/dist/deploy-command.d.ts.map +1 -0
- package/dist/deploy-command.js +164 -0
- package/dist/deploy-command.js.map +1 -0
- package/dist/launch-metadata.d.ts +2 -2
- package/dist/launch-metadata.d.ts.map +1 -1
- package/dist/launch-metadata.js +3 -4
- package/dist/launch-metadata.js.map +1 -1
- package/dist/launch-metadata.test.js +5 -32
- package/dist/launch-metadata.test.js.map +1 -1
- package/dist/local-personas.d.ts +8 -10
- package/dist/local-personas.d.ts.map +1 -1
- package/dist/local-personas.js +63 -163
- package/dist/local-personas.js.map +1 -1
- package/dist/local-personas.test.js +59 -332
- package/dist/local-personas.test.js.map +1 -1
- package/dist/persona-install.d.ts.map +1 -1
- package/dist/persona-install.js +4 -25
- package/dist/persona-install.js.map +1 -1
- package/dist/persona-install.test.js +4 -11
- package/dist/persona-install.test.js.map +1 -1
- package/package.json +4 -3
package/dist/cli.js
CHANGED
|
@@ -5,10 +5,11 @@ import { appendFileSync, closeSync, existsSync, mkdirSync, mkdtempSync, openSync
|
|
|
5
5
|
import { constants, homedir, tmpdir } from 'node:os';
|
|
6
6
|
import { dirname, isAbsolute, join, resolve as resolvePath } from 'node:path';
|
|
7
7
|
import { pathToFileURL } from 'node:url';
|
|
8
|
-
import { buildCleanupArtifacts, buildInstallArtifacts, buildInteractiveSpec, buildNonInteractiveSpec, detectHarnesses, formatDropWarnings, HARNESS_VALUES, materializeSkills, MissingPersonaInputError, PERSONA_TAGS,
|
|
8
|
+
import { buildCleanupArtifacts, buildInstallArtifacts, buildInteractiveSpec, buildNonInteractiveSpec, detectHarnesses, formatDropWarnings, HARNESS_VALUES, materializeSkills, MissingPersonaInputError, PERSONA_TAGS, renderPersonaInputs, resolveMcpServersLenient, resolvePersonaInputs, resolveSidecar, resolveStringMapLenient } from '@agentworkforce/persona-kit';
|
|
9
9
|
import { listBuiltInPersonas, personaCatalog, routingProfiles } from '@agentworkforce/workload-router';
|
|
10
10
|
import { createMount, readAgentDotfiles } from '@relayfile/local-mount';
|
|
11
11
|
import ora from 'ora';
|
|
12
|
+
import { runDeploy, runLogin } from './deploy-command.js';
|
|
12
13
|
import { startLaunchMetadataRecording } from './launch-metadata.js';
|
|
13
14
|
import { buildPersonaSourceDirectories, defaultCwdPersonaDir, formatPersonaSourceLabel, loadLocalPersonas, loadPersonaSourceConfig, normalizePersonaDir, savePersonaSourceConfig } from './local-personas.js';
|
|
14
15
|
import { installPersonas } from './persona-install.js';
|
|
@@ -23,7 +24,7 @@ is one of: built-in (bundled), cwd (./.agentworkforce/workforce/personas),
|
|
|
23
24
|
personal (~/.agentworkforce/workforce/personas), or dir:N (configured).
|
|
24
25
|
|
|
25
26
|
Commands:
|
|
26
|
-
create [flags] Opens persona-maker
|
|
27
|
+
create [flags] Opens persona-maker for creating a new
|
|
27
28
|
persona, with target path passed as persona inputs.
|
|
28
29
|
Flags:
|
|
29
30
|
--save-in-directory=<target>
|
|
@@ -40,12 +41,8 @@ Commands:
|
|
|
40
41
|
--install-in-repo Same behavior as agent.
|
|
41
42
|
--no-launch-metadata
|
|
42
43
|
Same behavior as agent.
|
|
43
|
-
agent [flags] <persona>
|
|
44
|
-
Run a persona.
|
|
45
|
-
With no @<tier>, the resolution order is:
|
|
46
|
-
routingProfiles.default.intents (built-in personas only)
|
|
47
|
-
→ persona.defaultTier (when set) → best-value. Drops into
|
|
48
|
-
an interactive harness session.
|
|
44
|
+
agent [flags] <persona>
|
|
45
|
+
Run a persona. Drops into an interactive harness session.
|
|
49
46
|
|
|
50
47
|
Flags:
|
|
51
48
|
--install-in-repo Disengage the sandbox mount and
|
|
@@ -87,26 +84,16 @@ Commands:
|
|
|
87
84
|
success, kept on failure for
|
|
88
85
|
inspection.
|
|
89
86
|
list [flags] List available personas from the cascade (cwd →
|
|
90
|
-
configured persona dirs → library).
|
|
91
|
-
one row per persona at the recommended tier for its
|
|
92
|
-
intent; pass --all to see every tier. Flags:
|
|
93
|
-
--all show every tier (overrides default)
|
|
87
|
+
configured persona dirs → library). Flags:
|
|
94
88
|
--json emit JSON instead of a table
|
|
95
|
-
--filter-rating <tier> only show this tier; disables
|
|
96
|
-
the recommended-only default
|
|
97
|
-
(${PERSONA_TIERS.join(' | ')})
|
|
98
89
|
--filter-harness <harness> only show this harness
|
|
99
90
|
(${HARNESS_VALUES.join(' | ')})
|
|
100
91
|
--filter-tag <tag> only show personas carrying this tag
|
|
101
92
|
(${PERSONA_TAGS.join(' | ')})
|
|
102
93
|
--no-display-description hide the DESCRIPTION column
|
|
103
|
-
show <persona>
|
|
104
|
-
Print the fully-resolved spec for a single persona,
|
|
94
|
+
show <persona> Print the fully-resolved spec for a single persona,
|
|
105
95
|
including which cascade layer defined it (cwd, user,
|
|
106
|
-
dir:<n>, library).
|
|
107
|
-
tier for the persona's intent; pass @<tier> to pick one,
|
|
108
|
-
or --all to see every tier. Flags:
|
|
109
|
-
--all include every tier (overrides default)
|
|
96
|
+
dir:<n>, library). Flags:
|
|
110
97
|
--json emit the resolved PersonaSpec as JSON
|
|
111
98
|
install [flags] <pkg|path>
|
|
112
99
|
Copy persona JSON files from an npm package or local
|
|
@@ -147,8 +134,8 @@ Examples:
|
|
|
147
134
|
agentworkforce create
|
|
148
135
|
agentworkforce create --save-in-directory=user
|
|
149
136
|
agentworkforce install @agentworkforce/personas-core --persona code-reviewer
|
|
150
|
-
agentworkforce agent code-reviewer
|
|
151
|
-
agentworkforce agent my-reviewer
|
|
137
|
+
agentworkforce agent code-reviewer
|
|
138
|
+
agentworkforce agent my-reviewer
|
|
152
139
|
agentworkforce list
|
|
153
140
|
agentworkforce show code-reviewer
|
|
154
141
|
agentworkforce install @agentrelay/personas --persona relay-orchestrator
|
|
@@ -173,7 +160,7 @@ function readPackageVersion() {
|
|
|
173
160
|
return pkg.version;
|
|
174
161
|
}
|
|
175
162
|
export const CLI_VERSION = readPackageVersion();
|
|
176
|
-
export const CREATE_SELECTOR = 'persona-maker
|
|
163
|
+
export const CREATE_SELECTOR = 'persona-maker';
|
|
177
164
|
const CREATE_INPUT_TARGET_DIR = 'TARGET_DIR';
|
|
178
165
|
const CREATE_INPUT_CREATE_MODE = 'CREATE_MODE';
|
|
179
166
|
const local = loadLocalPersonas();
|
|
@@ -236,31 +223,25 @@ function resolveSpec(key) {
|
|
|
236
223
|
};
|
|
237
224
|
}
|
|
238
225
|
function parseSelector(sel) {
|
|
226
|
+
// Catch legacy `@<tier>` selectors and point users at the new selector form.
|
|
227
|
+
// Tiers were removed; a persona's runtime fields now live at the top level.
|
|
239
228
|
const at = sel.indexOf('@');
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (tierRaw !== undefined && !PERSONA_TIERS.includes(tierRaw)) {
|
|
245
|
-
die(`Invalid tier "${tierRaw}". Must be one of: ${PERSONA_TIERS.join(', ')}`);
|
|
229
|
+
if (at !== -1) {
|
|
230
|
+
const suffix = sel.slice(at + 1);
|
|
231
|
+
die(`@<tier> selectors were removed; tiers are no longer part of the persona shape. ` +
|
|
232
|
+
`Use 'agentworkforce agent ${sel.slice(0, at)}' instead (drop "@${suffix}").`, false);
|
|
246
233
|
}
|
|
234
|
+
const key = sel;
|
|
235
|
+
if (!key)
|
|
236
|
+
die('Missing persona name');
|
|
247
237
|
const result = resolveSpec(key);
|
|
248
238
|
if ('error' in result)
|
|
249
239
|
die(result.error, false);
|
|
250
240
|
const kind = local.byId.has(key) ? 'local' : 'repo';
|
|
251
|
-
// Resolution order when no @<tier> is given: routingProfiles default for the
|
|
252
|
-
// persona's intent (built-ins only — local personas with custom intents miss
|
|
253
|
-
// the lookup and fall through), then the persona's own defaultTier, then
|
|
254
|
-
// 'best-value'. Mirrors `resolveShowTarget` and the `list` recommended-tier
|
|
255
|
-
// filter so all three commands agree on what "no tier" means.
|
|
256
|
-
const profileRule = kind === 'repo'
|
|
257
|
-
? routingProfiles.default.intents[result.intent]
|
|
258
|
-
: undefined;
|
|
259
|
-
const tier = (tierRaw ?? profileRule?.tier ?? result.defaultTier ?? 'best-value');
|
|
260
241
|
if (kind === 'local') {
|
|
261
|
-
return { kind, source: local.sources.get(result.id) ?? 'cwd', spec: result
|
|
242
|
+
return { kind, source: local.sources.get(result.id) ?? 'cwd', spec: result };
|
|
262
243
|
}
|
|
263
|
-
return { kind, source: 'library', spec: result
|
|
244
|
+
return { kind, source: 'library', spec: result };
|
|
264
245
|
}
|
|
265
246
|
/**
|
|
266
247
|
* Resolve the `<harness>` placeholder used in persona systemPrompts.
|
|
@@ -276,19 +257,22 @@ function parseSelector(sel) {
|
|
|
276
257
|
export function resolveSystemPromptPlaceholders(prompt, harness) {
|
|
277
258
|
return prompt.replaceAll('<harness>', harness);
|
|
278
259
|
}
|
|
279
|
-
function buildSelection(spec,
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
260
|
+
function buildSelection(spec, kind) {
|
|
261
|
+
const systemPrompt = resolveSystemPromptPlaceholders(spec.systemPrompt, spec.harness);
|
|
262
|
+
const sidecar = resolveSidecar(spec);
|
|
263
|
+
// Built-in personas: prefer the routing-profile rationale string so the
|
|
264
|
+
// selection carries the policy-explained "why" rather than a generic label.
|
|
265
|
+
const rationale = kind === 'local'
|
|
266
|
+
? `local-override: ${spec.id}`
|
|
267
|
+
: routingProfiles.default.intents[spec.intent]?.rationale ?? `cli: ${spec.id}`;
|
|
286
268
|
return {
|
|
287
269
|
personaId: spec.id,
|
|
288
|
-
|
|
289
|
-
|
|
270
|
+
harness: spec.harness,
|
|
271
|
+
model: spec.model,
|
|
272
|
+
systemPrompt,
|
|
273
|
+
harnessSettings: spec.harnessSettings,
|
|
290
274
|
skills: spec.skills,
|
|
291
|
-
rationale
|
|
275
|
+
rationale,
|
|
292
276
|
...(spec.inputs ? { inputs: spec.inputs } : {}),
|
|
293
277
|
...(spec.env ? { env: spec.env } : {}),
|
|
294
278
|
...(spec.mcpServers ? { mcpServers: spec.mcpServers } : {}),
|
|
@@ -426,7 +410,7 @@ async function runInstallOrThrow(command, label, cwd) {
|
|
|
426
410
|
}
|
|
427
411
|
}
|
|
428
412
|
function buildInstallContext(selection, options = {}) {
|
|
429
|
-
const plan = materializeSkills(selection.skills, selection.
|
|
413
|
+
const plan = materializeSkills(selection.skills, selection.harness, {
|
|
430
414
|
...(options.installRoot !== undefined ? { installRoot: options.installRoot } : {}),
|
|
431
415
|
...(options.repoRoot !== undefined ? { repoRoot: options.repoRoot } : {})
|
|
432
416
|
});
|
|
@@ -676,7 +660,7 @@ export function configureGitForMount(mountDir, patterns) {
|
|
|
676
660
|
* failing the whole session.
|
|
677
661
|
*/
|
|
678
662
|
export function loadSidecarForSelection(selection) {
|
|
679
|
-
const harness = selection.
|
|
663
|
+
const harness = selection.harness;
|
|
680
664
|
if (harness !== 'claude' && harness !== 'opencode' && harness !== 'codex')
|
|
681
665
|
return {};
|
|
682
666
|
if (harness === 'claude') {
|
|
@@ -803,7 +787,7 @@ export function decideCleanMode(harness, installInRepo = false) {
|
|
|
803
787
|
*/
|
|
804
788
|
function runDryRun(selection) {
|
|
805
789
|
const inputResolution = resolvePersonaInputs(selection.inputs, selection.inputValues, process.env);
|
|
806
|
-
const renderedSystemPrompt = renderPersonaInputs(selection.
|
|
790
|
+
const renderedSystemPrompt = renderPersonaInputs(selection.systemPrompt, inputResolution.values);
|
|
807
791
|
const renderedClaudeContent = selection.claudeMdContent !== undefined
|
|
808
792
|
? renderPersonaInputs(selection.claudeMdContent, inputResolution.values)
|
|
809
793
|
: undefined;
|
|
@@ -812,12 +796,12 @@ function runDryRun(selection) {
|
|
|
812
796
|
: undefined;
|
|
813
797
|
const effectiveSelection = {
|
|
814
798
|
...selection,
|
|
815
|
-
|
|
799
|
+
systemPrompt: renderedSystemPrompt,
|
|
816
800
|
...(renderedClaudeContent !== undefined ? { claudeMdContent: renderedClaudeContent } : {}),
|
|
817
801
|
...(renderedAgentsContent !== undefined ? { agentsMdContent: renderedAgentsContent } : {})
|
|
818
802
|
};
|
|
819
|
-
const {
|
|
820
|
-
process.stderr.write(`→ ${personaId}
|
|
803
|
+
const { personaId, harness, model, harnessSettings, systemPrompt } = effectiveSelection;
|
|
804
|
+
process.stderr.write(`→ ${personaId} via ${harness} (${model}) [DRY-RUN]\n`);
|
|
821
805
|
// Check 1: sidecar resolution. A loadSidecarForSelection warning means
|
|
822
806
|
// the persona points `claudeMd` / `agentsMd` at a file we couldn't
|
|
823
807
|
// read; the launch path degrades to a warning today, which silently
|
|
@@ -839,11 +823,11 @@ function runDryRun(selection) {
|
|
|
839
823
|
let spec;
|
|
840
824
|
try {
|
|
841
825
|
spec = buildInteractiveSpec({
|
|
842
|
-
harness
|
|
826
|
+
harness,
|
|
843
827
|
personaId,
|
|
844
|
-
model
|
|
845
|
-
systemPrompt
|
|
846
|
-
harnessSettings
|
|
828
|
+
model,
|
|
829
|
+
systemPrompt,
|
|
830
|
+
harnessSettings,
|
|
847
831
|
mcpServers: mcpResolution.servers,
|
|
848
832
|
permissions: effectiveSelection.permissions
|
|
849
833
|
});
|
|
@@ -860,7 +844,7 @@ function runDryRun(selection) {
|
|
|
860
844
|
// Dry-run runs each install inside a fresh tempDir (see `cwd: tempDir` on
|
|
861
845
|
// the spawnSync below). Pass repoRoot=process.cwd() so `local`-kind skills
|
|
862
846
|
// resolve their relative source paths against the real repo, not the tmp.
|
|
863
|
-
const plan = materializeSkills(effectiveSelection.skills,
|
|
847
|
+
const plan = materializeSkills(effectiveSelection.skills, harness, {
|
|
864
848
|
repoRoot: process.cwd()
|
|
865
849
|
});
|
|
866
850
|
if (plan.installs.length === 0) {
|
|
@@ -909,7 +893,7 @@ function runDryRun(selection) {
|
|
|
909
893
|
}
|
|
910
894
|
async function runInteractive(selection, options) {
|
|
911
895
|
const inputResolution = resolvePersonaInputs(selection.inputs, selection.inputValues, process.env);
|
|
912
|
-
const renderedSystemPrompt = renderPersonaInputs(selection.
|
|
896
|
+
const renderedSystemPrompt = renderPersonaInputs(selection.systemPrompt, inputResolution.values);
|
|
913
897
|
// Render input placeholders ($TARGET_DIR, ${CREATE_MODE}, …) inside the
|
|
914
898
|
// sidecar Content fields too. Personas that move heavy authoring guidance
|
|
915
899
|
// out of the systemPrompt and into AGENTS.md / CLAUDE.md still want
|
|
@@ -923,21 +907,18 @@ async function runInteractive(selection, options) {
|
|
|
923
907
|
: undefined;
|
|
924
908
|
const effectiveSelection = {
|
|
925
909
|
...selection,
|
|
926
|
-
|
|
927
|
-
...selection.runtime,
|
|
928
|
-
systemPrompt: renderedSystemPrompt
|
|
929
|
-
},
|
|
910
|
+
systemPrompt: renderedSystemPrompt,
|
|
930
911
|
...(renderedClaudeContent !== undefined ? { claudeMdContent: renderedClaudeContent } : {}),
|
|
931
912
|
...(renderedAgentsContent !== undefined ? { agentsMdContent: renderedAgentsContent } : {})
|
|
932
913
|
};
|
|
933
|
-
const {
|
|
914
|
+
const { personaId, harness, model, harnessSettings, systemPrompt } = effectiveSelection;
|
|
934
915
|
// `installRoot` (out-of-repo skill staging via `--plugin-dir`) is currently
|
|
935
916
|
// claude-only; the workload-router SDK throws if it's set for other
|
|
936
917
|
// harnesses. For opencode, we instead keep installs out of the repo by
|
|
937
918
|
// running them inside a @relayfile/local-mount sandbox (see `useClean`
|
|
938
919
|
// below). The --install-in-repo flag forces legacy in-repo installs
|
|
939
920
|
// across the board.
|
|
940
|
-
const useClean = decideCleanMode(
|
|
921
|
+
const useClean = decideCleanMode(harness, options.installInRepo === true).useClean;
|
|
941
922
|
// Per-persona CLAUDE.md / AGENTS.md: load the author content if any. The
|
|
942
923
|
// file is materialized into the mount inside onBeforeLaunch. Without a
|
|
943
924
|
// mount (--install-in-repo) we skip-and-warn — writing into the real cwd
|
|
@@ -954,9 +935,9 @@ async function runInteractive(selection, options) {
|
|
|
954
935
|
// A session dir is needed whenever we either (a) stage skills out-of-repo
|
|
955
936
|
// via claude's installRoot, or (b) open a mount. Both engage for claude/
|
|
956
937
|
// opencode by default; --install-in-repo disengages both.
|
|
957
|
-
const useSessionDir = !options.installInRepo && (
|
|
938
|
+
const useSessionDir = !options.installInRepo && (harness === 'claude' || useClean);
|
|
958
939
|
const sessionRoot = useSessionDir ? generateSessionRoot(personaId) : undefined;
|
|
959
|
-
const installRoot = sessionRoot &&
|
|
940
|
+
const installRoot = sessionRoot && harness === 'claude'
|
|
960
941
|
? sessionInstallRoot(sessionRoot)
|
|
961
942
|
: undefined;
|
|
962
943
|
// `repoRoot` lets the local skill provider (kind: 'local') resolve
|
|
@@ -968,7 +949,7 @@ async function runInteractive(selection, options) {
|
|
|
968
949
|
...(installRoot !== undefined ? { installRoot } : {}),
|
|
969
950
|
repoRoot: process.cwd()
|
|
970
951
|
});
|
|
971
|
-
process.stderr.write(`→ ${personaId}
|
|
952
|
+
process.stderr.write(`→ ${personaId} via ${harness} (${model})\n`);
|
|
972
953
|
const startLaunchMetadataForLaunch = (cwd = process.cwd()) => startLaunchMetadataRecording({
|
|
973
954
|
selection: effectiveSelection,
|
|
974
955
|
personaSpec: options.personaSpec,
|
|
@@ -998,16 +979,16 @@ async function runInteractive(selection, options) {
|
|
|
998
979
|
// INSIDE the mount so `.opencode/skills/`, `.agents/skills/`, prpm.lock,
|
|
999
980
|
// etc. land in the sandbox rather than the real repo. We defer it to
|
|
1000
981
|
// `onBeforeLaunch` below instead of pre-running here.
|
|
1001
|
-
const deferInstallToMount = useClean &&
|
|
982
|
+
const deferInstallToMount = useClean && harness !== 'claude' && install.commandString !== ':';
|
|
1002
983
|
if (install.commandString !== ':' && !deferInstallToMount) {
|
|
1003
984
|
await runInstall(install.command, installLabel);
|
|
1004
985
|
}
|
|
1005
986
|
const spec = buildInteractiveSpec({
|
|
1006
|
-
harness
|
|
987
|
+
harness,
|
|
1007
988
|
personaId,
|
|
1008
|
-
model
|
|
1009
|
-
systemPrompt
|
|
1010
|
-
harnessSettings
|
|
989
|
+
model,
|
|
990
|
+
systemPrompt,
|
|
991
|
+
harnessSettings,
|
|
1011
992
|
mcpServers: resolvedMcp,
|
|
1012
993
|
permissions: effectiveSelection.permissions,
|
|
1013
994
|
...(installRoot !== undefined ? { pluginDirs: [installRoot] } : {})
|
|
@@ -1040,8 +1021,8 @@ async function runInteractive(selection, options) {
|
|
|
1040
1021
|
// env refs are interpolated. We show the bin, model, and the *names* of
|
|
1041
1022
|
// the servers / permission fields so the user can verify the shape without
|
|
1042
1023
|
// leaking credentials to stderr or CI logs.
|
|
1043
|
-
const summary = [`model=${
|
|
1044
|
-
if (
|
|
1024
|
+
const summary = [`model=${model}`];
|
|
1025
|
+
if (harness === 'claude') {
|
|
1045
1026
|
const servers = Object.keys(resolvedMcp ?? {});
|
|
1046
1027
|
summary.push(`mcp-strict=${servers.length ? servers.join(',') : '(none)'}`);
|
|
1047
1028
|
if (effectiveSelection.permissions?.allow?.length) {
|
|
@@ -1086,7 +1067,7 @@ async function runInteractive(selection, options) {
|
|
|
1086
1067
|
const { ignoredPatterns, readonlyPatterns } = buildRelayfileMountPatterns({
|
|
1087
1068
|
projectDir: process.cwd(),
|
|
1088
1069
|
personaId,
|
|
1089
|
-
harness
|
|
1070
|
+
harness,
|
|
1090
1071
|
mount: effectiveSelection.mount,
|
|
1091
1072
|
configFilePaths: spec.configFiles.map((file) => file.path)
|
|
1092
1073
|
});
|
|
@@ -1225,7 +1206,7 @@ async function runInteractive(selection, options) {
|
|
|
1225
1206
|
const childCwd = handle.mountDir;
|
|
1226
1207
|
if (options.capture) {
|
|
1227
1208
|
options.capture.sessionCwd = childCwd;
|
|
1228
|
-
options.capture.harness =
|
|
1209
|
+
options.capture.harness = harness;
|
|
1229
1210
|
options.capture.startedAt = Date.now();
|
|
1230
1211
|
}
|
|
1231
1212
|
// Flip the SIGINT phase flag before spawn so a Ctrl-C arriving during
|
|
@@ -1294,7 +1275,7 @@ async function runInteractive(selection, options) {
|
|
|
1294
1275
|
}
|
|
1295
1276
|
const e = err;
|
|
1296
1277
|
if (e.code === 'ENOENT') {
|
|
1297
|
-
process.stderr.write(`Failed to spawn "${spec.bin}" inside sandbox mount: binary not found on PATH. Install the ${
|
|
1278
|
+
process.stderr.write(`Failed to spawn "${spec.bin}" inside sandbox mount: binary not found on PATH. Install the ${harness} CLI and retry.\n`);
|
|
1298
1279
|
return 127;
|
|
1299
1280
|
}
|
|
1300
1281
|
process.stderr.write(`Failed to launch sandbox mount: ${e.message}\n`);
|
|
@@ -1337,7 +1318,7 @@ async function runInteractive(selection, options) {
|
|
|
1337
1318
|
const launchMetadata = await startLaunchMetadataForLaunch();
|
|
1338
1319
|
if (options.capture) {
|
|
1339
1320
|
options.capture.sessionCwd = process.cwd();
|
|
1340
|
-
options.capture.harness =
|
|
1321
|
+
options.capture.harness = harness;
|
|
1341
1322
|
options.capture.startedAt = Date.now();
|
|
1342
1323
|
options.capture.stampEnrichment = { ...launchMetadata.metadata };
|
|
1343
1324
|
options.capture.stampingEnabled = launchMetadata.enabled;
|
|
@@ -1367,7 +1348,7 @@ async function runInteractive(selection, options) {
|
|
|
1367
1348
|
});
|
|
1368
1349
|
child.on('error', (err) => {
|
|
1369
1350
|
if (err.code === 'ENOENT') {
|
|
1370
|
-
process.stderr.write(`Failed to spawn "${spec.bin}": binary not found on PATH. Install the ${
|
|
1351
|
+
process.stderr.write(`Failed to spawn "${spec.bin}": binary not found on PATH. Install the ${harness} CLI and retry.\n`);
|
|
1371
1352
|
}
|
|
1372
1353
|
else {
|
|
1373
1354
|
process.stderr.write(`Failed to spawn "${spec.bin}": ${err.message}\n`);
|
|
@@ -1675,19 +1656,15 @@ function runPersonaInstall(args) {
|
|
|
1675
1656
|
function collectPersonaRows() {
|
|
1676
1657
|
const rows = [];
|
|
1677
1658
|
const pushSpec = (spec, source) => {
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
rating: tier,
|
|
1688
|
-
defaultTier: spec.defaultTier
|
|
1689
|
-
});
|
|
1690
|
-
}
|
|
1659
|
+
rows.push({
|
|
1660
|
+
persona: spec.id,
|
|
1661
|
+
source,
|
|
1662
|
+
harness: spec.harness,
|
|
1663
|
+
model: spec.model,
|
|
1664
|
+
intent: spec.intent,
|
|
1665
|
+
tags: spec.tags,
|
|
1666
|
+
description: spec.description
|
|
1667
|
+
});
|
|
1691
1668
|
};
|
|
1692
1669
|
const seen = new Set();
|
|
1693
1670
|
for (const [id, spec] of local.byId) {
|
|
@@ -1699,9 +1676,7 @@ function collectPersonaRows() {
|
|
|
1699
1676
|
continue;
|
|
1700
1677
|
pushSpec(spec, 'library');
|
|
1701
1678
|
}
|
|
1702
|
-
|
|
1703
|
-
return rows.sort((a, b) => a.persona.localeCompare(b.persona) ||
|
|
1704
|
-
(tierOrder.get(a.rating) - tierOrder.get(b.rating)));
|
|
1679
|
+
return rows.sort((a, b) => a.persona.localeCompare(b.persona));
|
|
1705
1680
|
}
|
|
1706
1681
|
function formatPersonaTable(rows, display) {
|
|
1707
1682
|
const headers = {
|
|
@@ -1709,18 +1684,14 @@ function formatPersonaTable(rows, display) {
|
|
|
1709
1684
|
source: 'SOURCE',
|
|
1710
1685
|
harness: 'HARNESS',
|
|
1711
1686
|
model: 'MODEL',
|
|
1712
|
-
rating: 'RATING',
|
|
1713
1687
|
tags: 'TAGS',
|
|
1714
1688
|
description: 'DESCRIPTION'
|
|
1715
1689
|
};
|
|
1716
1690
|
const rendered = rows.map((r) => ({
|
|
1717
1691
|
persona: r.persona,
|
|
1718
|
-
// Show the user-facing label (`built-in` / `repo` / `personal` / `dir:N`).
|
|
1719
|
-
// The internal cascade key is still in `--json` output for tooling.
|
|
1720
1692
|
source: formatPersonaSourceLabel(r.source),
|
|
1721
1693
|
harness: r.harness,
|
|
1722
1694
|
model: r.model,
|
|
1723
|
-
rating: r.rating,
|
|
1724
1695
|
tags: r.tags.join(','),
|
|
1725
1696
|
description: r.description
|
|
1726
1697
|
}));
|
|
@@ -1729,7 +1700,6 @@ function formatPersonaTable(rows, display) {
|
|
|
1729
1700
|
source: Math.max(headers.source.length, ...rendered.map((r) => r.source.length)),
|
|
1730
1701
|
harness: Math.max(headers.harness.length, ...rendered.map((r) => r.harness.length)),
|
|
1731
1702
|
model: Math.max(headers.model.length, ...rendered.map((r) => r.model.length)),
|
|
1732
|
-
rating: Math.max(headers.rating.length, ...rendered.map((r) => r.rating.length)),
|
|
1733
1703
|
tags: Math.max(headers.tags.length, ...rendered.map((r) => r.tags.length)),
|
|
1734
1704
|
description: headers.description.length
|
|
1735
1705
|
};
|
|
@@ -1738,9 +1708,8 @@ function formatPersonaTable(rows, display) {
|
|
|
1738
1708
|
widths.source +
|
|
1739
1709
|
widths.harness +
|
|
1740
1710
|
widths.model +
|
|
1741
|
-
widths.rating +
|
|
1742
1711
|
widths.tags +
|
|
1743
|
-
(
|
|
1712
|
+
(5 + (display.description ? 1 : 0) - 1) * 2;
|
|
1744
1713
|
const descBudget = Math.max(20, termWidth - fixed - 1);
|
|
1745
1714
|
const truncate = (s, n) => (s.length <= n ? s : s.slice(0, Math.max(1, n - 1)) + '…');
|
|
1746
1715
|
const line = (row) => {
|
|
@@ -1749,7 +1718,6 @@ function formatPersonaTable(rows, display) {
|
|
|
1749
1718
|
row.source.padEnd(widths.source),
|
|
1750
1719
|
row.harness.padEnd(widths.harness),
|
|
1751
1720
|
row.model.padEnd(widths.model),
|
|
1752
|
-
row.rating.padEnd(widths.rating),
|
|
1753
1721
|
row.tags.padEnd(widths.tags)
|
|
1754
1722
|
];
|
|
1755
1723
|
if (display.description) {
|
|
@@ -1761,11 +1729,8 @@ function formatPersonaTable(rows, display) {
|
|
|
1761
1729
|
}
|
|
1762
1730
|
function parseListArgs(args) {
|
|
1763
1731
|
let json = false;
|
|
1764
|
-
let filterRating;
|
|
1765
|
-
let filterRatingExplicit = false;
|
|
1766
1732
|
let filterHarness;
|
|
1767
1733
|
let filterTag;
|
|
1768
|
-
let showAll = false;
|
|
1769
1734
|
const display = { description: true };
|
|
1770
1735
|
const valueOf = (i, flag) => {
|
|
1771
1736
|
const v = args[i + 1];
|
|
@@ -1780,23 +1745,9 @@ function parseListArgs(args) {
|
|
|
1780
1745
|
json = true;
|
|
1781
1746
|
}
|
|
1782
1747
|
else if (arg === '-h' || arg === '--help') {
|
|
1783
|
-
process.stdout.write('Usage: agentworkforce list [--
|
|
1748
|
+
process.stdout.write('Usage: agentworkforce list [--json] [--filter-harness <harness>] [--filter-tag <tag>] [--no-display-description]\n');
|
|
1784
1749
|
process.exit(0);
|
|
1785
1750
|
}
|
|
1786
|
-
else if (arg === '--all' || arg === '--no-recommended') {
|
|
1787
|
-
showAll = true;
|
|
1788
|
-
}
|
|
1789
|
-
else if (arg === '--recommended') {
|
|
1790
|
-
showAll = false;
|
|
1791
|
-
}
|
|
1792
|
-
else if (arg === '--filter-rating') {
|
|
1793
|
-
const v = valueOf(i++, arg);
|
|
1794
|
-
if (!PERSONA_TIERS.includes(v)) {
|
|
1795
|
-
die(`list: invalid --filter-rating "${v}". Must be one of: ${PERSONA_TIERS.join(', ')}`);
|
|
1796
|
-
}
|
|
1797
|
-
filterRating = v;
|
|
1798
|
-
filterRatingExplicit = true;
|
|
1799
|
-
}
|
|
1800
1751
|
else if (arg === '--filter-harness') {
|
|
1801
1752
|
const v = valueOf(i++, arg);
|
|
1802
1753
|
if (!HARNESS_VALUES.includes(v)) {
|
|
@@ -1821,24 +1772,15 @@ function parseListArgs(args) {
|
|
|
1821
1772
|
die(`list: unexpected argument "${arg}".`);
|
|
1822
1773
|
}
|
|
1823
1774
|
}
|
|
1824
|
-
return { json,
|
|
1775
|
+
return { json, filterHarness, filterTag, display };
|
|
1825
1776
|
}
|
|
1826
1777
|
function runList(args) {
|
|
1827
|
-
const { json,
|
|
1828
|
-
const recommendedByIntent = routingProfiles.default.intents;
|
|
1829
|
-
const applyRecommended = !showAll && !filterRatingExplicit;
|
|
1778
|
+
const { json, filterHarness, filterTag, display } = parseListArgs(args);
|
|
1830
1779
|
const rows = collectPersonaRows().filter((r) => {
|
|
1831
|
-
if (filterRating && r.rating !== filterRating)
|
|
1832
|
-
return false;
|
|
1833
1780
|
if (filterHarness && r.harness !== filterHarness)
|
|
1834
1781
|
return false;
|
|
1835
1782
|
if (filterTag && !r.tags.includes(filterTag))
|
|
1836
1783
|
return false;
|
|
1837
|
-
if (applyRecommended) {
|
|
1838
|
-
const rule = recommendedByIntent[r.intent];
|
|
1839
|
-
if (r.rating !== (rule?.tier ?? r.defaultTier ?? 'best-value'))
|
|
1840
|
-
return false;
|
|
1841
|
-
}
|
|
1842
1784
|
return true;
|
|
1843
1785
|
});
|
|
1844
1786
|
if (json) {
|
|
@@ -1846,25 +1788,19 @@ function runList(args) {
|
|
|
1846
1788
|
}
|
|
1847
1789
|
else {
|
|
1848
1790
|
process.stdout.write(formatPersonaTable(rows, display));
|
|
1849
|
-
|
|
1850
|
-
const suffix = applyRecommended ? ' (recommended tier per intent; pass --all to see every tier)' : '';
|
|
1851
|
-
process.stdout.write(`\n${uniq} persona(s), ${rows.length} row(s)${suffix}.\n`);
|
|
1791
|
+
process.stdout.write(`\n${rows.length} persona(s).\n`);
|
|
1852
1792
|
}
|
|
1853
1793
|
process.exit(0);
|
|
1854
1794
|
}
|
|
1855
1795
|
function parseShowArgs(args) {
|
|
1856
1796
|
let json = false;
|
|
1857
|
-
let all = false;
|
|
1858
1797
|
let selector;
|
|
1859
1798
|
for (const arg of args) {
|
|
1860
1799
|
if (arg === '--json') {
|
|
1861
1800
|
json = true;
|
|
1862
1801
|
}
|
|
1863
|
-
else if (arg === '--all') {
|
|
1864
|
-
all = true;
|
|
1865
|
-
}
|
|
1866
1802
|
else if (arg === '-h' || arg === '--help') {
|
|
1867
|
-
process.stdout.write('Usage: agentworkforce show <persona>
|
|
1803
|
+
process.stdout.write('Usage: agentworkforce show <persona> [--json]\n');
|
|
1868
1804
|
process.exit(0);
|
|
1869
1805
|
}
|
|
1870
1806
|
else if (arg.startsWith('--')) {
|
|
@@ -1879,24 +1815,16 @@ function parseShowArgs(args) {
|
|
|
1879
1815
|
}
|
|
1880
1816
|
if (!selector)
|
|
1881
1817
|
die('show: missing persona name.');
|
|
1882
|
-
return { selector, json
|
|
1818
|
+
return { selector, json };
|
|
1883
1819
|
}
|
|
1884
|
-
function resolveShowTarget(selector
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
if (!key)
|
|
1889
|
-
die('show: missing persona name before "@".');
|
|
1890
|
-
let explicitTier;
|
|
1891
|
-
if (tierRaw !== undefined) {
|
|
1892
|
-
if (!PERSONA_TIERS.includes(tierRaw)) {
|
|
1893
|
-
die(`show: invalid tier "${tierRaw}". Must be one of: ${PERSONA_TIERS.join(', ')}`);
|
|
1894
|
-
}
|
|
1895
|
-
explicitTier = tierRaw;
|
|
1896
|
-
if (all) {
|
|
1897
|
-
die('show: --all cannot be combined with an explicit @<tier> suffix.');
|
|
1898
|
-
}
|
|
1820
|
+
function resolveShowTarget(selector) {
|
|
1821
|
+
if (selector.includes('@')) {
|
|
1822
|
+
die('show: @<tier> selectors were removed; tiers are no longer part of the persona shape. ' +
|
|
1823
|
+
`Use 'agentworkforce show ${selector.slice(0, selector.indexOf('@'))}' instead.`, false);
|
|
1899
1824
|
}
|
|
1825
|
+
const key = selector;
|
|
1826
|
+
if (!key)
|
|
1827
|
+
die('show: missing persona name.');
|
|
1900
1828
|
const localSpec = local.byId.get(key);
|
|
1901
1829
|
let spec;
|
|
1902
1830
|
let source = 'library';
|
|
@@ -1921,18 +1849,7 @@ function resolveShowTarget(selector, all) {
|
|
|
1921
1849
|
die(result.error, false);
|
|
1922
1850
|
spec = result;
|
|
1923
1851
|
}
|
|
1924
|
-
|
|
1925
|
-
if (all) {
|
|
1926
|
-
tiers = [...PERSONA_TIERS];
|
|
1927
|
-
}
|
|
1928
|
-
else if (explicitTier) {
|
|
1929
|
-
tiers = [explicitTier];
|
|
1930
|
-
}
|
|
1931
|
-
else {
|
|
1932
|
-
const rule = routingProfiles.default.intents[spec.intent];
|
|
1933
|
-
tiers = [rule?.tier ?? spec.defaultTier ?? 'best-value'];
|
|
1934
|
-
}
|
|
1935
|
-
return { spec, source, tiers, explicitTier };
|
|
1852
|
+
return { spec, source };
|
|
1936
1853
|
}
|
|
1937
1854
|
function indent(text, prefix) {
|
|
1938
1855
|
return text
|
|
@@ -1940,17 +1857,13 @@ function indent(text, prefix) {
|
|
|
1940
1857
|
.map((line) => (line.length > 0 ? prefix + line : line))
|
|
1941
1858
|
.join('\n');
|
|
1942
1859
|
}
|
|
1943
|
-
function formatPersonaShow(spec, source
|
|
1860
|
+
function formatPersonaShow(spec, source) {
|
|
1944
1861
|
const lines = [];
|
|
1945
1862
|
lines.push(`PERSONA ${spec.id}`);
|
|
1946
1863
|
lines.push(`SOURCE ${source}`);
|
|
1947
1864
|
lines.push(`INTENT ${spec.intent}`);
|
|
1948
1865
|
lines.push(`TAGS ${spec.tags.length ? spec.tags.join(', ') : '(none)'}`);
|
|
1949
1866
|
lines.push(`DESCRIPTION ${spec.description}`);
|
|
1950
|
-
if (spec.defaultTier) {
|
|
1951
|
-
lines.push(`DEFAULT TIER ${spec.defaultTier}`);
|
|
1952
|
-
}
|
|
1953
|
-
lines.push(`TIERS SHOWN ${tiers.join(', ')}${tierNote ? ` (${tierNote})` : ''}`);
|
|
1954
1867
|
lines.push('');
|
|
1955
1868
|
lines.push('SKILLS');
|
|
1956
1869
|
if (spec.skills.length === 0) {
|
|
@@ -2039,46 +1952,36 @@ function formatPersonaShow(spec, source, tiers, tierNote) {
|
|
|
2039
1952
|
for (const k of envKeys)
|
|
2040
1953
|
lines.push(` ${k}=${spec.env[k]}`);
|
|
2041
1954
|
}
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
lines.push(`
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
}
|
|
2053
|
-
if (rt.harnessSettings.approvalPolicy) {
|
|
2054
|
-
lines.push(` approvals: ${rt.harnessSettings.approvalPolicy}`);
|
|
2055
|
-
}
|
|
2056
|
-
if (rt.harnessSettings.workspaceWriteNetworkAccess !== undefined) {
|
|
2057
|
-
lines.push(` network: ${rt.harnessSettings.workspaceWriteNetworkAccess}`);
|
|
2058
|
-
}
|
|
2059
|
-
if (rt.harnessSettings.webSearch !== undefined) {
|
|
2060
|
-
lines.push(` webSearch: ${rt.harnessSettings.webSearch}`);
|
|
2061
|
-
}
|
|
2062
|
-
lines.push(' systemPrompt:');
|
|
2063
|
-
lines.push(indent(rt.systemPrompt, ' '));
|
|
1955
|
+
lines.push('');
|
|
1956
|
+
lines.push('RUNTIME');
|
|
1957
|
+
lines.push(` harness: ${spec.harness}`);
|
|
1958
|
+
lines.push(` model: ${spec.model}`);
|
|
1959
|
+
lines.push(` reasoning: ${spec.harnessSettings.reasoning}`);
|
|
1960
|
+
lines.push(` timeout: ${spec.harnessSettings.timeoutSeconds}s`);
|
|
1961
|
+
if (spec.harnessSettings.sandboxMode) {
|
|
1962
|
+
lines.push(` sandbox: ${spec.harnessSettings.sandboxMode}`);
|
|
1963
|
+
}
|
|
1964
|
+
if (spec.harnessSettings.approvalPolicy) {
|
|
1965
|
+
lines.push(` approvals: ${spec.harnessSettings.approvalPolicy}`);
|
|
2064
1966
|
}
|
|
1967
|
+
if (spec.harnessSettings.workspaceWriteNetworkAccess !== undefined) {
|
|
1968
|
+
lines.push(` network: ${spec.harnessSettings.workspaceWriteNetworkAccess}`);
|
|
1969
|
+
}
|
|
1970
|
+
if (spec.harnessSettings.webSearch !== undefined) {
|
|
1971
|
+
lines.push(` webSearch: ${spec.harnessSettings.webSearch}`);
|
|
1972
|
+
}
|
|
1973
|
+
lines.push(' systemPrompt:');
|
|
1974
|
+
lines.push(indent(spec.systemPrompt, ' '));
|
|
2065
1975
|
return lines.join('\n') + '\n';
|
|
2066
1976
|
}
|
|
2067
1977
|
function runShow(args) {
|
|
2068
|
-
const { selector, json
|
|
2069
|
-
const { spec, source
|
|
2070
|
-
const tierNote = all
|
|
2071
|
-
? 'all tiers'
|
|
2072
|
-
: explicitTier
|
|
2073
|
-
? 'explicit @<tier>'
|
|
2074
|
-
: 'recommended for intent; pass --all or @<tier> to override';
|
|
1978
|
+
const { selector, json } = parseShowArgs(args);
|
|
1979
|
+
const { spec, source } = resolveShowTarget(selector);
|
|
2075
1980
|
if (json) {
|
|
2076
|
-
|
|
2077
|
-
const projected = { ...spec, tiers: projectedTiers };
|
|
2078
|
-
process.stdout.write(JSON.stringify({ source, spec: projected }, null, 2) + '\n');
|
|
1981
|
+
process.stdout.write(JSON.stringify({ source, spec }, null, 2) + '\n');
|
|
2079
1982
|
}
|
|
2080
1983
|
else {
|
|
2081
|
-
process.stdout.write(formatPersonaShow(spec, source
|
|
1984
|
+
process.stdout.write(formatPersonaShow(spec, source));
|
|
2082
1985
|
}
|
|
2083
1986
|
process.exit(0);
|
|
2084
1987
|
}
|
|
@@ -2165,7 +2068,7 @@ function saveDefaultCreateTarget(target) {
|
|
|
2165
2068
|
async function runAgentSelector(selector, flags, inputValues) {
|
|
2166
2069
|
const target = parseSelector(selector);
|
|
2167
2070
|
const selection = {
|
|
2168
|
-
...buildSelection(target.spec, target.
|
|
2071
|
+
...buildSelection(target.spec, target.kind),
|
|
2169
2072
|
...(inputValues ? { inputValues } : {})
|
|
2170
2073
|
};
|
|
2171
2074
|
if (flags.dryRun) {
|
|
@@ -2302,9 +2205,7 @@ const ALLOWED_SET_PATHS = [
|
|
|
2302
2205
|
'agentsMdContent',
|
|
2303
2206
|
'claudeMdContent',
|
|
2304
2207
|
'tags',
|
|
2305
|
-
'
|
|
2306
|
-
'tiers.best-value.systemPrompt',
|
|
2307
|
-
'tiers.minimum.systemPrompt'
|
|
2208
|
+
'systemPrompt'
|
|
2308
2209
|
];
|
|
2309
2210
|
/**
|
|
2310
2211
|
* Allowlist of dot-paths the improver may rewrite via `op: "append"`.
|
|
@@ -2750,15 +2651,14 @@ async function runPersonaImprover(args) {
|
|
|
2750
2651
|
if (!improverSpec) {
|
|
2751
2652
|
throw new Error('built-in persona "persona-improver" is not registered in the catalog');
|
|
2752
2653
|
}
|
|
2753
|
-
const
|
|
2754
|
-
const selection = buildSelection(improverSpec, tier, 'repo');
|
|
2654
|
+
const selection = buildSelection(improverSpec, 'repo');
|
|
2755
2655
|
const inputValues = {
|
|
2756
2656
|
PERSONA_FILE_PATH: args.personaFilePath,
|
|
2757
2657
|
SESSION_TRANSCRIPT_PATH: args.transcriptPath,
|
|
2758
2658
|
PROPOSALS_OUTPUT_PATH: args.proposalsOutputPath
|
|
2759
2659
|
};
|
|
2760
2660
|
const inputResolution = resolvePersonaInputs(selection.inputs, inputValues, process.env);
|
|
2761
|
-
const renderedSystemPrompt = renderPersonaInputs(selection.
|
|
2661
|
+
const renderedSystemPrompt = renderPersonaInputs(selection.systemPrompt, inputResolution.values);
|
|
2762
2662
|
const callerEnv = { ...process.env, ...inputResolution.values };
|
|
2763
2663
|
const envResolution = resolveStringMapLenient(selection.env, callerEnv, 'env');
|
|
2764
2664
|
const mcpResolution = resolveMcpServersLenient(selection.mcpServers, callerEnv);
|
|
@@ -2770,11 +2670,11 @@ async function runPersonaImprover(args) {
|
|
|
2770
2670
|
].join('\n');
|
|
2771
2671
|
const task = `${taskBody}\n\nRun inputs:\n${JSON.stringify(inputValues, null, 2)}`;
|
|
2772
2672
|
const spec = buildNonInteractiveSpec({
|
|
2773
|
-
harness: selection.
|
|
2673
|
+
harness: selection.harness,
|
|
2774
2674
|
personaId: selection.personaId,
|
|
2775
|
-
model: selection.
|
|
2675
|
+
model: selection.model,
|
|
2776
2676
|
systemPrompt: renderedSystemPrompt,
|
|
2777
|
-
harnessSettings: selection.
|
|
2677
|
+
harnessSettings: selection.harnessSettings,
|
|
2778
2678
|
mcpServers: mcpResolution.servers,
|
|
2779
2679
|
permissions: selection.permissions,
|
|
2780
2680
|
task
|
|
@@ -2801,8 +2701,8 @@ async function runPersonaImprover(args) {
|
|
|
2801
2701
|
}
|
|
2802
2702
|
}
|
|
2803
2703
|
};
|
|
2804
|
-
const timeoutMs = selection.
|
|
2805
|
-
? selection.
|
|
2704
|
+
const timeoutMs = selection.harnessSettings.timeoutSeconds
|
|
2705
|
+
? selection.harnessSettings.timeoutSeconds * 1000
|
|
2806
2706
|
: undefined;
|
|
2807
2707
|
let captureResult;
|
|
2808
2708
|
try {
|
|
@@ -2885,15 +2785,8 @@ export function parseProposals(raw) {
|
|
|
2885
2785
|
throw new Error(`proposals[${idx}] must be an object`);
|
|
2886
2786
|
}
|
|
2887
2787
|
const p = item;
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
}
|
|
2891
|
-
if (typeof p.summary !== 'string' || !p.summary.trim()) {
|
|
2892
|
-
throw new Error(`proposals[${idx}].summary must be a non-empty string`);
|
|
2893
|
-
}
|
|
2894
|
-
if (typeof p.rationale !== 'string') {
|
|
2895
|
-
throw new Error(`proposals[${idx}].rationale must be a string`);
|
|
2896
|
-
}
|
|
2788
|
+
const id = normalizeNonEmptyString(p.id) ?? `proposal-${idx + 1}`;
|
|
2789
|
+
const rationale = typeof p.rationale === 'string' ? p.rationale.trim() : '';
|
|
2897
2790
|
if (!Array.isArray(p.patches) || p.patches.length === 0) {
|
|
2898
2791
|
throw new Error(`proposals[${idx}].patches must be a non-empty array`);
|
|
2899
2792
|
}
|
|
@@ -2913,10 +2806,11 @@ export function parseProposals(raw) {
|
|
|
2913
2806
|
assertAllowedImproverPatch(patch, `proposals[${idx}].patches[${pidx}]`);
|
|
2914
2807
|
patches.push(patch);
|
|
2915
2808
|
}
|
|
2809
|
+
const summary = normalizeProposalSummary(p.summary, id, patches);
|
|
2916
2810
|
proposals.push({
|
|
2917
|
-
id
|
|
2918
|
-
summary
|
|
2919
|
-
rationale
|
|
2811
|
+
id,
|
|
2812
|
+
summary,
|
|
2813
|
+
rationale,
|
|
2920
2814
|
patches
|
|
2921
2815
|
});
|
|
2922
2816
|
}
|
|
@@ -2927,6 +2821,54 @@ export function parseProposals(raw) {
|
|
|
2927
2821
|
proposals
|
|
2928
2822
|
};
|
|
2929
2823
|
}
|
|
2824
|
+
function normalizeNonEmptyString(value) {
|
|
2825
|
+
if (typeof value !== 'string')
|
|
2826
|
+
return undefined;
|
|
2827
|
+
const trimmed = value.trim();
|
|
2828
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
2829
|
+
}
|
|
2830
|
+
function normalizeProposalSummary(value, id, patches) {
|
|
2831
|
+
const explicit = normalizeNonEmptyString(value);
|
|
2832
|
+
if (explicit)
|
|
2833
|
+
return explicit;
|
|
2834
|
+
const fromId = humanizeProposalId(id);
|
|
2835
|
+
if (fromId)
|
|
2836
|
+
return fromId;
|
|
2837
|
+
return summarizeProposalPatches(patches);
|
|
2838
|
+
}
|
|
2839
|
+
function humanizeProposalId(id) {
|
|
2840
|
+
const trimmed = id.trim();
|
|
2841
|
+
if (!trimmed || /^p\d+$/i.test(trimmed) || /^proposal-\d+$/i.test(trimmed)) {
|
|
2842
|
+
return undefined;
|
|
2843
|
+
}
|
|
2844
|
+
const words = trimmed.replace(/[-_]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
2845
|
+
if (!words)
|
|
2846
|
+
return undefined;
|
|
2847
|
+
return `${words.slice(0, 1).toUpperCase()}${words.slice(1)}`;
|
|
2848
|
+
}
|
|
2849
|
+
function summarizeProposalPatches(patches) {
|
|
2850
|
+
if (patches.length === 1)
|
|
2851
|
+
return summarizeSingleProposalPatch(patches[0]);
|
|
2852
|
+
return `Apply ${patches.length} persona updates`;
|
|
2853
|
+
}
|
|
2854
|
+
function summarizeSingleProposalPatch(patch) {
|
|
2855
|
+
if (patch.op === 'append' && patch.path === 'skills')
|
|
2856
|
+
return 'Add skill';
|
|
2857
|
+
if (patch.path.startsWith('inputs.')) {
|
|
2858
|
+
return `Add ${patch.path.slice('inputs.'.length)} input`;
|
|
2859
|
+
}
|
|
2860
|
+
if (patch.path === 'description')
|
|
2861
|
+
return 'Update description';
|
|
2862
|
+
if (patch.path === 'agentsMdContent')
|
|
2863
|
+
return 'Update AGENTS.md guidance';
|
|
2864
|
+
if (patch.path === 'claudeMdContent')
|
|
2865
|
+
return 'Update CLAUDE.md guidance';
|
|
2866
|
+
if (patch.path === 'tags')
|
|
2867
|
+
return 'Update tags';
|
|
2868
|
+
if (patch.path === 'systemPrompt')
|
|
2869
|
+
return 'Update system prompt';
|
|
2870
|
+
return patch.op === 'append' ? `Append to ${patch.path}` : `Update ${patch.path}`;
|
|
2871
|
+
}
|
|
2930
2872
|
/**
|
|
2931
2873
|
* Walk improver proposals one-by-one over the TTY. Returns only the
|
|
2932
2874
|
* accepted proposals; the caller applies the patches. Supports:
|
|
@@ -3310,6 +3252,14 @@ export async function main() {
|
|
|
3310
3252
|
if (subcommand === 'pick') {
|
|
3311
3253
|
await runPick(rest);
|
|
3312
3254
|
}
|
|
3255
|
+
if (subcommand === 'deploy') {
|
|
3256
|
+
await runDeploy(rest);
|
|
3257
|
+
return;
|
|
3258
|
+
}
|
|
3259
|
+
if (subcommand === 'login') {
|
|
3260
|
+
await runLogin(rest);
|
|
3261
|
+
return;
|
|
3262
|
+
}
|
|
3313
3263
|
if (subcommand !== 'agent') {
|
|
3314
3264
|
die(`Unknown subcommand "${subcommand}".`);
|
|
3315
3265
|
}
|
|
@@ -3420,7 +3370,7 @@ export function parseCreateArgs(args) {
|
|
|
3420
3370
|
}
|
|
3421
3371
|
const [unexpected] = positional;
|
|
3422
3372
|
if (unexpected) {
|
|
3423
|
-
die(`create: unexpected argument "${unexpected}". The create command always runs ${CREATE_SELECTOR}; use "agentworkforce agent <persona>
|
|
3373
|
+
die(`create: unexpected argument "${unexpected}". The create command always runs ${CREATE_SELECTOR}; use "agentworkforce agent <persona>" to run another persona.`);
|
|
3424
3374
|
}
|
|
3425
3375
|
const target = resolveCreateTarget(flags.saveInDirectory);
|
|
3426
3376
|
ensureCreateTargetDir(target);
|