@delorenj/pjangler 1.2.33 → 1.3.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/dist/index.js +295 -130
- package/dist/mcp-server.js +297 -129
- package/package.json +1 -1
- package/templates/commonproject/template/.agents/hooks/README.md +10 -10
- package/templates/commonproject/template/.agents/hooks/hooks.master.json +1 -1
- package/templates/commonproject/template/.agents/hooks/sync.py +4 -4
- package/templates/commonproject/template/.agents/local.example.json +1 -1
- package/templates/commonproject/template/.mise/scripts/hindsight-setup.sh +1 -1
- package/templates/commonproject/template/mise.toml.jinja +11 -11
- package/templates/hermes-agent/copier.yml +27 -4
- package/templates/hermes-agent/template/.runtime-scaffold/README.md +11 -10
- package/templates/hermes-agent/template/.scripts/01-config.sh +4 -4
- package/templates/hermes-agent/template/.scripts/10-hermes-profile.sh +7 -12
- package/templates/hermes-agent/template/.scripts/20-runtime-repo.sh +37 -32
- package/templates/hermes-agent/template/.scripts/30-telegram.sh +18 -4
- package/templates/hermes-agent/template/.scripts/70-systemd.sh +72 -12
- package/templates/hermes-agent/template/.scripts/80-registry.sh +13 -3
- package/templates/hermes-agent/template/.scripts/_lib.sh +12 -5
- package/templates/hermes-agent/template/.scripts/config.example.toml +7 -4
- package/templates/hermes-agent/template/.scripts/credential-launch.sh +81 -0
- package/templates/hermes-agent/template/.scripts/heartbeat.sh +2 -1
- package/templates/hermes-agent/template/.scripts/providers/plane.sh +30 -4
- package/templates/hermes-agent/template/.scripts/secret-scan.py +82 -16
- package/templates/hermes-agent/template/SOUL.md.jinja +4 -4
- package/templates/hermes-agent/template/hermes.jinja +36 -7
- package/templates/hermes-agent/template/role.yaml.jinja +15 -5
package/dist/mcp-server.js
CHANGED
|
@@ -878,8 +878,26 @@ var OP_INJECT_SCRIPT = `'{{config_root}}/${MATERIALIZE_ENV_SCRIPT_REL}'`;
|
|
|
878
878
|
var PROVISION_PACKS_SCRIPT_REL = ".mise/scripts/provision-packs.py";
|
|
879
879
|
var LEGACY_PROVISION_SCRIPT_REL = ".mise/scripts/provision-bmad-skills.py";
|
|
880
880
|
var SYNC_SKILLS_SCRIPT_REL = ".mise/scripts/sync-skills.py";
|
|
881
|
-
var
|
|
881
|
+
var LINK_AGENTFILES_TASK = "link:agentfiles";
|
|
882
|
+
var SKILLS_SYNC_TASK = "skills:sync";
|
|
883
|
+
var PROVISION_PACKS_TASK = "skills:provision:packs";
|
|
882
884
|
var LEGACY_PROVISION_TASK = "skills-provision-bmad";
|
|
885
|
+
var RETIRED_TASK_RENAMES = [
|
|
886
|
+
["link-agentfiles", LINK_AGENTFILES_TASK],
|
|
887
|
+
["skills-sync", SKILLS_SYNC_TASK],
|
|
888
|
+
["skills-provision-packs", PROVISION_PACKS_TASK],
|
|
889
|
+
["hooks-sync", "hooks:sync"],
|
|
890
|
+
["hooks-check", "hooks:check"],
|
|
891
|
+
["hooks-uninstall", "hooks:uninstall"],
|
|
892
|
+
["hindsight-setup", "hindsight:setup"]
|
|
893
|
+
];
|
|
894
|
+
function taskHeader(name) {
|
|
895
|
+
return `[tasks."${name}"]`;
|
|
896
|
+
}
|
|
897
|
+
function taskHeaderPattern(name) {
|
|
898
|
+
const esc = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
899
|
+
return new RegExp(`^\\[tasks\\.(?:"${esc}"|${esc})\\]$`);
|
|
900
|
+
}
|
|
883
901
|
var PROVISION_PACKS_SCRIPT = `python3 '{{config_root}}/${PROVISION_PACKS_SCRIPT_REL}'`;
|
|
884
902
|
var LEGACY_PROVISION_BMAD_SKILLS_SCRIPT = `python3 '{{config_root}}/${LEGACY_PROVISION_SCRIPT_REL}'`;
|
|
885
903
|
var SYNC_SKILLS_SCRIPT = `python3 '{{config_root}}/${SYNC_SKILLS_SCRIPT_REL}' --scope project`;
|
|
@@ -913,22 +931,22 @@ var LINK_AGENTFILES_HOOK_ENTRIES = [
|
|
|
913
931
|
];
|
|
914
932
|
var LINK_AGENTFILES_WATCH_TASK_BLOCK = `[[watch_files]]
|
|
915
933
|
patterns = ["AGENTS.md"]
|
|
916
|
-
task = "
|
|
934
|
+
task = "${LINK_AGENTFILES_TASK}"
|
|
917
935
|
|
|
918
936
|
[[watch_files]]
|
|
919
937
|
patterns = [".agents/skills.json"]
|
|
920
|
-
task = "
|
|
938
|
+
task = "${SKILLS_SYNC_TASK}"
|
|
921
939
|
|
|
922
|
-
|
|
940
|
+
${taskHeader(LINK_AGENTFILES_TASK)}
|
|
923
941
|
description = "Symlink all agent files to AGENTS.md"
|
|
924
942
|
run = "'{{config_root}}/.mise/scripts/link-agentfiles.sh'"
|
|
925
943
|
|
|
926
|
-
|
|
944
|
+
${taskHeader(SKILLS_SYNC_TASK)}
|
|
927
945
|
description = "Sync skills from manifest to local CLI dirs"
|
|
928
946
|
depends = ["${PROVISION_PACKS_TASK}"]
|
|
929
947
|
run = ${JSON.stringify(SYNC_SKILLS_SCRIPT)}
|
|
930
948
|
|
|
931
|
-
|
|
949
|
+
${taskHeader(PROVISION_PACKS_TASK)}
|
|
932
950
|
description = "Provision every Skillex pack declared in .agents/skills.json"
|
|
933
951
|
run = ${JSON.stringify(PROVISION_PACKS_SCRIPT)}`;
|
|
934
952
|
var VERSIONING_BLOCK = `# >>> mise-versioning >>> (managed block \u2014 do not edit by hand; re-run init to update)
|
|
@@ -1090,6 +1108,7 @@ function discoverRoles(repoRoot) {
|
|
|
1090
1108
|
ticketProviderName: yamlGet(text2, "ticket_provider.name"),
|
|
1091
1109
|
ticketProviderBoardId: yamlGet(text2, "ticket_provider.board_id"),
|
|
1092
1110
|
ticketProviderIdentifier: yamlGet(text2, "plane.identifier"),
|
|
1111
|
+
bloodbankEnabled: yamlGet(text2, "bloodbank.enabled"),
|
|
1093
1112
|
deploymentSystemd: yamlGet(text2, "deployment.systemd"),
|
|
1094
1113
|
legacyReconcileEnabled: yamlGet(text2, "reconcile.enabled"),
|
|
1095
1114
|
legacyReconcileGraceHours: yamlGet(text2, "reconcile.grace_hours"),
|
|
@@ -2292,10 +2311,41 @@ function upsertOpInjectHook(text2) {
|
|
|
2292
2311
|
[OP_INJECT_SCRIPT]
|
|
2293
2312
|
);
|
|
2294
2313
|
}
|
|
2314
|
+
function retiredTaskNameIssues(text2) {
|
|
2315
|
+
const issues = [];
|
|
2316
|
+
for (const [oldName, newName] of RETIRED_TASK_RENAMES) {
|
|
2317
|
+
const esc = oldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2318
|
+
const present = new RegExp(
|
|
2319
|
+
`^\\[tasks\\.(?:"${esc}"|${esc})\\]|^\\s*task\\s*=\\s*"${esc}"|^\\s*depends\\s*=\\s*\\[[^\\]]*"${esc}"`,
|
|
2320
|
+
"m"
|
|
2321
|
+
).test(text2);
|
|
2322
|
+
if (present) issues.push(`mise.toml still uses the retired task name "${oldName}" (renamed to "${newName}")`);
|
|
2323
|
+
}
|
|
2324
|
+
return issues;
|
|
2325
|
+
}
|
|
2326
|
+
function renameRetiredMiseTasks(text2) {
|
|
2327
|
+
let out = text2;
|
|
2328
|
+
for (const [oldName, newName] of RETIRED_TASK_RENAMES) {
|
|
2329
|
+
const esc = oldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2330
|
+
out = out.replace(new RegExp(`^\\[tasks\\.(?:"${esc}"|${esc})\\]`, "gm"), taskHeader(newName));
|
|
2331
|
+
out = out.replace(new RegExp(`^(\\s*task\\s*=\\s*)"${esc}"`, "gm"), `$1"${newName}"`);
|
|
2332
|
+
out = out.replace(new RegExp(`\\bmise run ${esc}\\b`, "g"), `mise run ${newName}`);
|
|
2333
|
+
}
|
|
2334
|
+
return out.replace(/^(\s*depends\s*=\s*)(\[[^\]]*\])/gm, (_whole, head, arr) => {
|
|
2335
|
+
let next = arr;
|
|
2336
|
+
for (const [oldName, newName] of RETIRED_TASK_RENAMES) {
|
|
2337
|
+
next = next.split(`"${oldName}"`).join(`"${newName}"`);
|
|
2338
|
+
}
|
|
2339
|
+
return head + next;
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2295
2342
|
function upsertLinkAgentfilesBlock(text2, ctx) {
|
|
2296
|
-
const withPath = upsertMisePath(text2, requiredMisePathEntries(ctx));
|
|
2297
|
-
let cleaned = removeTomlSection(withPath,
|
|
2343
|
+
const withPath = upsertMisePath(renameRetiredMiseTasks(text2), requiredMisePathEntries(ctx));
|
|
2344
|
+
let cleaned = removeTomlSection(withPath, taskHeaderPattern(LINK_AGENTFILES_TASK), /link-agentfiles/, { includePrecedingComments: false });
|
|
2345
|
+
cleaned = removeTomlSection(cleaned, /^\[tasks\.link-agentfiles\]$/, /link-agentfiles/, { includePrecedingComments: false });
|
|
2346
|
+
cleaned = removeTomlSection(cleaned, taskHeaderPattern(SKILLS_SYNC_TASK), void 0, { includePrecedingComments: false });
|
|
2298
2347
|
cleaned = removeTomlSection(cleaned, /^\[tasks\.skills-sync\]$/, void 0, { includePrecedingComments: false });
|
|
2348
|
+
cleaned = removeTomlSection(cleaned, taskHeaderPattern(PROVISION_PACKS_TASK), void 0, { includePrecedingComments: false });
|
|
2299
2349
|
cleaned = removeTomlSection(cleaned, /^\[tasks\.skills-provision-packs\]$/, void 0, { includePrecedingComments: false });
|
|
2300
2350
|
cleaned = removeTomlSection(cleaned, /^\[tasks\.skills-provision-bmad\]$/, void 0, { includePrecedingComments: false });
|
|
2301
2351
|
cleaned = removeTomlSection(cleaned, /^\[tasks\.link-project-skills-to-clis\]$/, void 0, { includePrecedingComments: false });
|
|
@@ -2537,7 +2587,7 @@ You are **${role.displayName || role.agentId}** \u2014 a Hermes agent provisione
|
|
|
2537
2587
|
|
|
2538
2588
|
## Scope
|
|
2539
2589
|
|
|
2540
|
-
You operate only within the working directory of \`${role.repo}\`.
|
|
2590
|
+
You operate only within the working directory of \`${role.repo}\`. HERMES_HOME is the real named profile at \`~/.hermes/profiles/${role.profileName || role.agentId}\`; shared config/auth/skills remain linked to fleet truth while owned state lives in ignored \`./runtime/\`. The launcher supplies the project root through process-local \`TERMINAL_CWD\` and never persists it into shared config.
|
|
2541
2591
|
|
|
2542
2592
|
## Tone
|
|
2543
2593
|
|
|
@@ -2552,51 +2602,62 @@ ${roleSpecific}
|
|
|
2552
2602
|
Your memory is stored locally at \`./runtime/memories/\`. Use durable memory deliberately and keep \`memories/MEMORY.md\` current.
|
|
2553
2603
|
`;
|
|
2554
2604
|
}
|
|
2555
|
-
function renderHermesWrapper(role) {
|
|
2556
|
-
return
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
if
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2605
|
+
function renderHermesWrapper(role, templateRoleDir) {
|
|
2606
|
+
return readText(join3(templateRoleDir, "hermes.jinja")).replace(/\{\{\s*agent_id\s*\}\}/g, role.agentId);
|
|
2607
|
+
}
|
|
2608
|
+
function templateFiles(sourceDir, current = sourceDir) {
|
|
2609
|
+
if (!existsSync2(current)) return [];
|
|
2610
|
+
const files = [];
|
|
2611
|
+
for (const entry of readdirSync2(current, { withFileTypes: true })) {
|
|
2612
|
+
if (entry.name === "__pycache__" || entry.name.endsWith(".pyc") || entry.name.endsWith(".pyo")) continue;
|
|
2613
|
+
const sourcePath = join3(current, entry.name);
|
|
2614
|
+
if (entry.isDirectory()) files.push(...templateFiles(sourceDir, sourcePath));
|
|
2615
|
+
else if (entry.isFile()) files.push(relative2(sourceDir, sourcePath));
|
|
2616
|
+
}
|
|
2617
|
+
return files.sort();
|
|
2618
|
+
}
|
|
2619
|
+
function managedHermesScaffoldRoles(ctx) {
|
|
2620
|
+
const discovered = discoverRoles(ctx.repoRoot);
|
|
2621
|
+
const declared = readDeclaredAgents(ctx).filter((entry) => entry.role === "pm" || entry.role === "director");
|
|
2622
|
+
if (declared.length === 0) {
|
|
2623
|
+
const orchestrators = discovered.filter((role) => role.role === "pm" || role.role === "director");
|
|
2624
|
+
const blockers2 = orchestrators.filter((role) => roleBloodbankEnabled(role) === null).map((role) => `${relative2(ctx.repoRoot, role.roleYamlPath)} bloodbank.enabled must be the strict YAML boolean true or false`);
|
|
2625
|
+
return {
|
|
2626
|
+
roles: orchestrators.filter((role) => roleBloodbankEnabled(role) !== null),
|
|
2627
|
+
blockers: blockers2
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
2630
|
+
const roles = [];
|
|
2631
|
+
const blockers = [];
|
|
2632
|
+
for (const entry of declared) {
|
|
2633
|
+
if (!entry.roleDir) {
|
|
2634
|
+
blockers.push(`agents.${entry.agentId}.role_dir missing`);
|
|
2635
|
+
continue;
|
|
2636
|
+
}
|
|
2637
|
+
const roleDir = resolve3(ctx.repoRoot, entry.roleDir);
|
|
2638
|
+
if (!isContainedBy(ctx.repoRoot, roleDir)) {
|
|
2639
|
+
blockers.push(`agents.${entry.agentId}.role_dir resolves outside the project`);
|
|
2640
|
+
continue;
|
|
2641
|
+
}
|
|
2642
|
+
const role = discovered.find((candidate) => resolve3(candidate.roleDir) === roleDir);
|
|
2643
|
+
if (!role) {
|
|
2644
|
+
blockers.push(`agents.${entry.agentId}.role_dir ${entry.roleDir} missing role.yaml`);
|
|
2645
|
+
continue;
|
|
2646
|
+
}
|
|
2647
|
+
if (role.agentId !== entry.agentId || role.role !== entry.role) {
|
|
2648
|
+
blockers.push(`agents.${entry.agentId} identity does not match ${entry.roleDir}/role.yaml`);
|
|
2649
|
+
continue;
|
|
2650
|
+
}
|
|
2651
|
+
if (roleBloodbankEnabled(role) === null) {
|
|
2652
|
+
blockers.push(`${entry.roleDir}/role.yaml bloodbank.enabled must be the strict YAML boolean true or false`);
|
|
2653
|
+
continue;
|
|
2654
|
+
}
|
|
2655
|
+
roles.push(role);
|
|
2656
|
+
}
|
|
2657
|
+
return { roles, blockers };
|
|
2658
|
+
}
|
|
2659
|
+
function renderSentinelPrompt(role, templateRoleDir) {
|
|
2660
|
+
return readText(join3(templateRoleDir, ".scripts", "sentinel.prompt.md.jinja")).replace(/\{\{\s*agent_id\s*\}\}/g, role.agentId).replace(/\{\{\s*role\s*\}\}/g, role.role).replace(/\{\{\s*target_repo\s*\}\}/g, role.repo).replace(/\{\{\s*display_name\s*\}\}/g, role.displayName || role.agentId).replace(/\{\{\s*ticket_provider\s*\}\}/g, role.ticketProviderName || "plane");
|
|
2600
2661
|
}
|
|
2601
2662
|
function copyMissingRecursive(sourceDir, targetDir, changedFiles, dryRun, skip) {
|
|
2602
2663
|
if (!existsSync2(sourceDir)) return;
|
|
@@ -2693,6 +2754,8 @@ function upsertRegistryEntry(role, homeDir, changedFiles, dryRun) {
|
|
|
2693
2754
|
const path = registryPath(homeDir);
|
|
2694
2755
|
const current = safeReadText(path) ?? "# Hermes agent fleet registry.\n# One entry per provisioned agent. Managed by hermes-agent-template/.scripts/80-registry.sh.\nschema_version: 1\nagents: {}\n";
|
|
2695
2756
|
if (current.includes(`${role.agentId}:`)) return null;
|
|
2757
|
+
const enabled = roleBloodbankEnabled(role);
|
|
2758
|
+
if (enabled === null) return null;
|
|
2696
2759
|
const block = ` ${role.agentId}:
|
|
2697
2760
|
repo: ${role.repo}
|
|
2698
2761
|
role: ${role.role}
|
|
@@ -2708,6 +2771,7 @@ function upsertRegistryEntry(role, homeDir, changedFiles, dryRun) {
|
|
|
2708
2771
|
identifier: ${ctxEscape(role.ticketProviderIdentifier)}
|
|
2709
2772
|
runtime_repo: ${ctxEscape(role.runtimeRepo)}
|
|
2710
2773
|
bloodbank:
|
|
2774
|
+
enabled: ${enabled ? "true" : "false"}
|
|
2711
2775
|
gateway_scope: fleet
|
|
2712
2776
|
target_agent_id: ${role.agentId}
|
|
2713
2777
|
systemd:
|
|
@@ -2720,6 +2784,11 @@ ${block}`) : `${current.replace(/\s*$/, "\n")}${block}`;
|
|
|
2720
2784
|
if (!dryRun) writeText(path, next);
|
|
2721
2785
|
return path;
|
|
2722
2786
|
}
|
|
2787
|
+
function roleBloodbankEnabled(role) {
|
|
2788
|
+
if (role.bloodbankEnabled === "" || role.bloodbankEnabled === "false") return false;
|
|
2789
|
+
if (role.bloodbankEnabled === "true") return true;
|
|
2790
|
+
return null;
|
|
2791
|
+
}
|
|
2723
2792
|
function profileMetaInheritsDefault(path) {
|
|
2724
2793
|
const text2 = safeReadText(path);
|
|
2725
2794
|
return Boolean(
|
|
@@ -3186,8 +3255,30 @@ function dropDeclaredAgent(ctx, agentId, changedFiles, details) {
|
|
|
3186
3255
|
if (!ctx.dryRun) writeText(path, `${JSON.stringify(doc, null, 2)}
|
|
3187
3256
|
`);
|
|
3188
3257
|
}
|
|
3189
|
-
function
|
|
3190
|
-
|
|
3258
|
+
function isProfileHomeExpr(assigned) {
|
|
3259
|
+
const bare = assigned.replace(/^["']|["']$/g, "");
|
|
3260
|
+
return bare === "$FLEET_HOME/profiles/$PROFILE_NAME" || /^\$\{?HERMES_FLEET_HOME.*\}?\/profiles\//.test(bare) || /\/\.hermes\/profiles\/[^/]+$/.test(bare);
|
|
3261
|
+
}
|
|
3262
|
+
function rewriteLauncher(text2, profileName) {
|
|
3263
|
+
let next = text2;
|
|
3264
|
+
const assigned = /^HERMES_HOME=(.*)$/m.exec(next)?.[1]?.trim();
|
|
3265
|
+
if (assigned !== void 0 && !isProfileHomeExpr(assigned)) {
|
|
3266
|
+
const name = profileName ? `\${HERMES_PROFILE_NAME:-${profileName}}` : '${HERMES_PROFILE_NAME:-$(basename "$ROLE_DIR")}';
|
|
3267
|
+
next = next.replace(
|
|
3268
|
+
/^HERMES_HOME=(.*)$/m,
|
|
3269
|
+
[
|
|
3270
|
+
`RUNTIME_HOME=$1`,
|
|
3271
|
+
`FLEET_HOME="\${HERMES_FLEET_HOME:-$HOME/.hermes}"`,
|
|
3272
|
+
`PROFILE_NAME="${name}"`,
|
|
3273
|
+
`# Singleton-runtime contract: HERMES_HOME MUST be the named profile dir.`,
|
|
3274
|
+
`HERMES_HOME="$FLEET_HOME/profiles/$PROFILE_NAME"`
|
|
3275
|
+
].join("\n")
|
|
3276
|
+
);
|
|
3277
|
+
next = next.replace(
|
|
3278
|
+
/if \[\[ ! -d "\$HERMES_HOME" \]\]; then\n(\s*)echo "hermes: local runtime not provisioned at \$HERMES_HOME"/,
|
|
3279
|
+
'if [[ ! -d "$RUNTIME_HOME" ]]; then\n$1echo "hermes: local runtime not provisioned at $RUNTIME_HOME"'
|
|
3280
|
+
);
|
|
3281
|
+
}
|
|
3191
3282
|
next = next.replace(/^HERMES_OAUTH_FILE=.*\n/m, "");
|
|
3192
3283
|
next = next.replace(/\s*HERMES_OAUTH_FILE="\$HERMES_OAUTH_FILE"/g, "");
|
|
3193
3284
|
next = next.replace(
|
|
@@ -3394,7 +3485,8 @@ function createMiseChecks() {
|
|
|
3394
3485
|
if (missingPathValues.length) details.push(`[env]._.path should include ${missingPathValues.join(", ")}`);
|
|
3395
3486
|
if (!text2.includes("'{{config_root}}/.mise/scripts/link-agentfiles.sh'")) details.push("link-agentfiles hook must use single-quoted {{config_root}} guard");
|
|
3396
3487
|
if (!text2.includes('patterns = ["AGENTS.md"]')) details.push("watch_files must monitor AGENTS.md");
|
|
3397
|
-
if (!text2.includes(
|
|
3488
|
+
if (!text2.includes(`task = "${LINK_AGENTFILES_TASK}"`)) details.push(`watch_files must dispatch the ${LINK_AGENTFILES_TASK} task`);
|
|
3489
|
+
details.push(...retiredTaskNameIssues(text2));
|
|
3398
3490
|
return {
|
|
3399
3491
|
id: "mise.config-root",
|
|
3400
3492
|
title: "mise config_root + AGENTS link hooks",
|
|
@@ -3442,7 +3534,7 @@ function createMiseChecks() {
|
|
|
3442
3534
|
status: changedFiles.length ? "applied" : "noop",
|
|
3443
3535
|
summary: changedFiles.length ? "Updated mise AGENTS-linking contract" : "No changes required",
|
|
3444
3536
|
changedFiles,
|
|
3445
|
-
details: changedFiles.length ? [
|
|
3537
|
+
details: changedFiles.length ? [`Normalized hooks/watch_files/tasks."${LINK_AGENTFILES_TASK}" block and script`] : []
|
|
3446
3538
|
};
|
|
3447
3539
|
}
|
|
3448
3540
|
},
|
|
@@ -3653,8 +3745,9 @@ function createAgentHooksChecks() {
|
|
|
3653
3745
|
details.push("mise.toml still invokes the missing bare sync-skills.py executable");
|
|
3654
3746
|
}
|
|
3655
3747
|
if (!mise?.includes('patterns = [".agents/skills.json"]')) details.push("mise.toml should watch .agents/skills.json");
|
|
3656
|
-
if (!mise?.includes(
|
|
3657
|
-
if (!mise?.includes(`depends = ["${PROVISION_PACKS_TASK}"]`)) details.push(
|
|
3748
|
+
if (!mise?.includes(taskHeader(SKILLS_SYNC_TASK))) details.push(`mise.toml should define a ${SKILLS_SYNC_TASK} task`);
|
|
3749
|
+
if (!mise?.includes(`depends = ["${PROVISION_PACKS_TASK}"]`)) details.push(`${SKILLS_SYNC_TASK} task should depend on ${PROVISION_PACKS_TASK}`);
|
|
3750
|
+
if (mise) details.push(...retiredTaskNameIssues(mise));
|
|
3658
3751
|
for (const [rel, label] of [
|
|
3659
3752
|
[PROVISION_PACKS_SCRIPT_REL, "Skillex pack provisioning script"],
|
|
3660
3753
|
[SYNC_SKILLS_SCRIPT_REL, "Project-local skills sync engine"]
|
|
@@ -4547,75 +4640,90 @@ function createHermesChecks() {
|
|
|
4547
4640
|
return [
|
|
4548
4641
|
{
|
|
4549
4642
|
id: "hermes.pm-scaffold",
|
|
4550
|
-
title: "Hermes
|
|
4643
|
+
title: "Hermes orchestrator scaffold parity",
|
|
4551
4644
|
audit: (ctx) => {
|
|
4552
|
-
const
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
return { id: "hermes.pm-scaffold", title: "Hermes PM scaffold parity", status: "skip", summary: "No pm role present", details: [], fixable: false };
|
|
4645
|
+
const selection = managedHermesScaffoldRoles(ctx);
|
|
4646
|
+
if (selection.roles.length === 0 && selection.blockers.length === 0) {
|
|
4647
|
+
return { id: "hermes.pm-scaffold", title: "Hermes orchestrator scaffold parity", status: "skip", summary: "No provisioned pm or director role present", details: [], fixable: false };
|
|
4556
4648
|
}
|
|
4557
|
-
const details = [];
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4649
|
+
const details = [...selection.blockers];
|
|
4650
|
+
const templateRoleDir = join3(ctx.pjanglerRoot, "templates", "hermes-agent", "template");
|
|
4651
|
+
const managedScripts = templateFiles(join3(templateRoleDir, ".scripts")).filter((rel) => rel !== "sentinel.prompt.md.jinja");
|
|
4652
|
+
for (const role of selection.roles) {
|
|
4653
|
+
const prefix = role.agentId || role.role;
|
|
4654
|
+
for (const rel of ["role.yaml", "SOUL.md", ".runtime-scaffold/README.md", "runtime/memories/MEMORY.md"]) {
|
|
4655
|
+
if (!existsSync2(join3(role.roleDir, rel))) details.push(`${prefix}: missing ${relative2(ctx.repoRoot, join3(role.roleDir, rel))}`);
|
|
4656
|
+
}
|
|
4657
|
+
const wrapper = join3(role.roleDir, "hermes");
|
|
4658
|
+
const expectedWrapper = renderHermesWrapper(role, templateRoleDir);
|
|
4659
|
+
if (!existsSync2(wrapper)) details.push(`${prefix}: missing ${relative2(ctx.repoRoot, wrapper)}`);
|
|
4660
|
+
else if (safeReadText(wrapper) !== expectedWrapper) details.push(`${prefix}: stale ${relative2(ctx.repoRoot, wrapper)}`);
|
|
4661
|
+
const expectedIgnore = readText(join3(templateRoleDir, ".gitignore.jinja")).replace(/\{\{\s*role\s*\}\}/g, role.role);
|
|
4662
|
+
const ignorePath = join3(role.roleDir, ".gitignore");
|
|
4663
|
+
if (!existsSync2(ignorePath)) details.push(`${prefix}: missing ${relative2(ctx.repoRoot, ignorePath)}`);
|
|
4664
|
+
else if (safeReadText(ignorePath) !== expectedIgnore) details.push(`${prefix}: stale ${relative2(ctx.repoRoot, ignorePath)}`);
|
|
4665
|
+
for (const rel of managedScripts) {
|
|
4666
|
+
const source = join3(templateRoleDir, ".scripts", rel);
|
|
4667
|
+
const target = join3(role.roleDir, ".scripts", rel);
|
|
4668
|
+
if (!existsSync2(target)) details.push(`${prefix}: missing ${relative2(ctx.repoRoot, target)}`);
|
|
4669
|
+
else if (safeReadText(target) !== readText(source)) details.push(`${prefix}: stale ${relative2(ctx.repoRoot, target)}`);
|
|
4670
|
+
}
|
|
4671
|
+
const promptPath = join3(role.roleDir, ".scripts", "sentinel.prompt.md");
|
|
4672
|
+
if (!existsSync2(promptPath)) details.push(`${prefix}: missing ${relative2(ctx.repoRoot, promptPath)}`);
|
|
4673
|
+
else if (safeReadText(promptPath) !== renderSentinelPrompt(role, templateRoleDir)) details.push(`${prefix}: stale ${relative2(ctx.repoRoot, promptPath)}`);
|
|
4674
|
+
if (hasRuntimeSubmoduleMapping(ctx.repoRoot, role)) details.push(`${prefix}: .gitmodules contains retired ${role.role} runtime submodule mapping`);
|
|
4675
|
+
if (!profileMetaInheritsDefault(join3(role.roleDir, "runtime", "profile.yaml"))) details.push(`${prefix}: runtime/profile.yaml missing inherited default config metadata`);
|
|
4676
|
+
const registry = safeReadText(registryPath(ctx.homeDir));
|
|
4677
|
+
if (!registry?.includes(`${role.agentId}:`)) details.push(`fleet registry missing ${role.agentId}`);
|
|
4566
4678
|
}
|
|
4567
|
-
const registry = safeReadText(registryPath(ctx.homeDir));
|
|
4568
|
-
if (!registry?.includes(`${role.agentId}:`)) details.push(`fleet registry missing ${role.agentId}`);
|
|
4569
4679
|
return {
|
|
4570
4680
|
id: "hermes.pm-scaffold",
|
|
4571
|
-
title: "Hermes
|
|
4681
|
+
title: "Hermes orchestrator scaffold parity",
|
|
4572
4682
|
status: details.length === 0 ? "pass" : "fail",
|
|
4573
|
-
summary: details.length === 0 ?
|
|
4683
|
+
summary: details.length === 0 ? `${selection.roles.length} orchestrator scaffold(s) verified` : `${details.length} orchestrator scaffold issue(s) detected`,
|
|
4574
4684
|
details,
|
|
4575
|
-
fixable:
|
|
4685
|
+
fixable: selection.blockers.length === 0
|
|
4576
4686
|
};
|
|
4577
4687
|
},
|
|
4578
4688
|
migrate: (ctx, finding) => {
|
|
4579
|
-
const
|
|
4689
|
+
const selection = managedHermesScaffoldRoles(ctx);
|
|
4580
4690
|
const changedFiles = [];
|
|
4581
4691
|
const details = [];
|
|
4582
|
-
if (
|
|
4583
|
-
return { id: finding.id, title: finding.title, status: "blocked", summary: "
|
|
4692
|
+
if (selection.blockers.length > 0) {
|
|
4693
|
+
return { id: finding.id, title: finding.title, status: "blocked", summary: "Provisioned orchestrator manifest is invalid", changedFiles, details: selection.blockers };
|
|
4584
4694
|
}
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
if (!retirement.ok) {
|
|
4588
|
-
return {
|
|
4589
|
-
id: finding.id,
|
|
4590
|
-
title: finding.title,
|
|
4591
|
-
status: "blocked",
|
|
4592
|
-
summary: "Failed to retire PM runtime submodule metadata safely",
|
|
4593
|
-
changedFiles,
|
|
4594
|
-
details: [retirement.error ?? "unknown runtime retirement failure"]
|
|
4595
|
-
};
|
|
4695
|
+
if (selection.roles.length === 0) {
|
|
4696
|
+
return { id: finding.id, title: finding.title, status: "blocked", summary: "No provisioned pm or director role present", changedFiles, details: [] };
|
|
4596
4697
|
}
|
|
4597
4698
|
const templateRoleDir = join3(ctx.pjanglerRoot, "templates", "hermes-agent", "template");
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4699
|
+
const managedScripts = templateFiles(join3(templateRoleDir, ".scripts")).filter((rel) => rel !== "sentinel.prompt.md.jinja");
|
|
4700
|
+
for (const role of selection.roles) {
|
|
4701
|
+
const retirement = retireRuntimeSubmodule(ctx.repoRoot, role, changedFiles, ctx.dryRun);
|
|
4702
|
+
details.push(...retirement.details);
|
|
4703
|
+
if (!retirement.ok) {
|
|
4704
|
+
return { id: finding.id, title: finding.title, status: "blocked", summary: `Failed to retire ${role.role} runtime submodule metadata safely`, changedFiles, details: [retirement.error ?? "unknown runtime retirement failure"] };
|
|
4705
|
+
}
|
|
4706
|
+
if (!existsSync2(join3(role.roleDir, "SOUL.md"))) writeIfDifferent(join3(role.roleDir, "SOUL.md"), renderSoul(role), ctx.dryRun, changedFiles);
|
|
4707
|
+
writeIfDifferent(join3(role.roleDir, "hermes"), renderHermesWrapper(role, templateRoleDir), ctx.dryRun, changedFiles, 493);
|
|
4708
|
+
writeIfDifferent(join3(role.roleDir, ".gitignore"), readText(join3(templateRoleDir, ".gitignore.jinja")).replace(/\{\{\s*role\s*\}\}/g, role.role), ctx.dryRun, changedFiles);
|
|
4709
|
+
copyMissingRecursive(join3(templateRoleDir, ".runtime-scaffold"), join3(role.roleDir, ".runtime-scaffold"), changedFiles, ctx.dryRun);
|
|
4710
|
+
copyMissingRecursive(join3(templateRoleDir, ".runtime-scaffold"), join3(role.roleDir, "runtime"), changedFiles, ctx.dryRun);
|
|
4711
|
+
for (const rel of managedScripts) {
|
|
4712
|
+
const source = join3(templateRoleDir, ".scripts", rel);
|
|
4713
|
+
const executable = (lstatSync2(source).mode & 73) !== 0;
|
|
4714
|
+
writeIfDifferent(join3(role.roleDir, ".scripts", rel), readText(source), ctx.dryRun, changedFiles, executable ? 493 : void 0);
|
|
4715
|
+
}
|
|
4716
|
+
writeIfDifferent(join3(role.roleDir, ".scripts", "sentinel.prompt.md"), renderSentinelPrompt(role, templateRoleDir), ctx.dryRun, changedFiles);
|
|
4717
|
+
const profileMetaUpdated = upsertInheritedProfileMeta(join3(role.roleDir, "runtime", "profile.yaml"), changedFiles, ctx.dryRun);
|
|
4718
|
+
if (profileMetaUpdated) details.push(`updated ${profileMetaUpdated}`);
|
|
4719
|
+
const registryUpdated = upsertRegistryEntry(role, ctx.homeDir, changedFiles, ctx.dryRun);
|
|
4720
|
+
if (registryUpdated) details.push(`updated ${registryUpdated}`);
|
|
4609
4721
|
}
|
|
4610
|
-
const profileMetaUpdated = upsertInheritedProfileMeta(join3(role.roleDir, "runtime", "profile.yaml"), changedFiles, ctx.dryRun);
|
|
4611
|
-
if (profileMetaUpdated) details.push(`updated ${profileMetaUpdated}`);
|
|
4612
|
-
const registryUpdated = upsertRegistryEntry(role, ctx.homeDir, changedFiles, ctx.dryRun);
|
|
4613
|
-
if (registryUpdated) details.push(`updated ${registryUpdated}`);
|
|
4614
4722
|
return {
|
|
4615
4723
|
id: finding.id,
|
|
4616
4724
|
title: finding.title,
|
|
4617
4725
|
status: changedFiles.length ? "applied" : "noop",
|
|
4618
|
-
summary: changedFiles.length ?
|
|
4726
|
+
summary: changedFiles.length ? `${selection.roles.length} orchestrator scaffold(s) normalized` : "No changes required",
|
|
4619
4727
|
changedFiles,
|
|
4620
4728
|
details
|
|
4621
4729
|
};
|
|
@@ -4922,8 +5030,9 @@ function createHermesChecks() {
|
|
|
4922
5030
|
if (text2 === null) {
|
|
4923
5031
|
details.push(`launcher missing: ${relative2(ctx.repoRoot, launcher)}`);
|
|
4924
5032
|
} else {
|
|
4925
|
-
|
|
4926
|
-
|
|
5033
|
+
const assigned = /^HERMES_HOME=(.*)$/m.exec(text2)?.[1]?.trim();
|
|
5034
|
+
if (assigned !== void 0 && !isProfileHomeExpr(assigned)) {
|
|
5035
|
+
details.push(`launcher sets HERMES_HOME=${assigned} instead of the named profile dir (disables shared auth + profile identity): ${relative2(ctx.repoRoot, launcher)}`);
|
|
4927
5036
|
}
|
|
4928
5037
|
if (/HERMES_OAUTH_FILE/.test(text2)) {
|
|
4929
5038
|
details.push(`launcher exports HERMES_OAUTH_FILE, which Hermes does not implement (dead config): ${relative2(ctx.repoRoot, launcher)}`);
|
|
@@ -4961,9 +5070,16 @@ function createHermesChecks() {
|
|
|
4961
5070
|
const launcher = join3(role.roleDir, "hermes");
|
|
4962
5071
|
const text2 = safeReadText(launcher);
|
|
4963
5072
|
if (text2 !== null) {
|
|
4964
|
-
const
|
|
5073
|
+
const before = /^HERMES_HOME=(.*)$/m.exec(text2)?.[1]?.trim();
|
|
5074
|
+
const rewritten = rewriteLauncher(text2, role.profileName || role.agentId);
|
|
4965
5075
|
if (rewritten !== text2) {
|
|
4966
|
-
|
|
5076
|
+
const rel = relative2(ctx.repoRoot, launcher);
|
|
5077
|
+
if (before !== void 0 && !isProfileHomeExpr(before)) {
|
|
5078
|
+
details.push(`rewrite launcher HERMES_HOME ${before} -> ${plan.profileDir}: ${rel}`);
|
|
5079
|
+
}
|
|
5080
|
+
if (/HERMES_OAUTH_FILE/.test(text2)) {
|
|
5081
|
+
details.push(`strip dead HERMES_OAUTH_FILE export: ${rel}`);
|
|
5082
|
+
}
|
|
4967
5083
|
writeIfDifferent(launcher, rewritten, ctx.dryRun, changedFiles, 493);
|
|
4968
5084
|
}
|
|
4969
5085
|
}
|
|
@@ -5000,6 +5116,7 @@ function createHermesChecks() {
|
|
|
5000
5116
|
audit: (ctx) => {
|
|
5001
5117
|
const roles = discoverRoles(ctx.repoRoot);
|
|
5002
5118
|
const details = [];
|
|
5119
|
+
let malformedRoleGate = false;
|
|
5003
5120
|
const registryPath2 = join3(ctx.homeDir, ".hermes", "agents-registry.yaml");
|
|
5004
5121
|
const registry = readRegistry(registryPath2);
|
|
5005
5122
|
if (!registry) {
|
|
@@ -5036,6 +5153,11 @@ function createHermesChecks() {
|
|
|
5036
5153
|
details.push(`.project.json declares agent "${extra}" that no role.yaml claims`);
|
|
5037
5154
|
}
|
|
5038
5155
|
for (const role of roles) {
|
|
5156
|
+
const expectedBloodbankEnabled = roleBloodbankEnabled(role);
|
|
5157
|
+
if (expectedBloodbankEnabled === null) {
|
|
5158
|
+
details.push(`${relative2(ctx.repoRoot, role.roleYamlPath)} bloodbank.enabled must be the strict YAML boolean true or false`);
|
|
5159
|
+
malformedRoleGate = true;
|
|
5160
|
+
}
|
|
5039
5161
|
const entry = registry[role.agentId];
|
|
5040
5162
|
if (!entry) {
|
|
5041
5163
|
details.push(`registry is missing an entry for ${role.agentId}`);
|
|
@@ -5050,6 +5172,11 @@ function createHermesChecks() {
|
|
|
5050
5172
|
details.push(`registry hermes.bin for ${role.agentId} does not exist: ${bin}`);
|
|
5051
5173
|
}
|
|
5052
5174
|
const bloodbank = entry.bloodbank ?? {};
|
|
5175
|
+
if (typeof bloodbank.enabled !== "boolean") {
|
|
5176
|
+
details.push(`registry entry for ${role.agentId} bloodbank.enabled must be a strict boolean`);
|
|
5177
|
+
} else if (expectedBloodbankEnabled !== null && bloodbank.enabled !== expectedBloodbankEnabled) {
|
|
5178
|
+
details.push(`registry entry for ${role.agentId} bloodbank.enabled must match explicit role value ${expectedBloodbankEnabled}`);
|
|
5179
|
+
}
|
|
5053
5180
|
if (bloodbank.gateway_scope !== "fleet" || bloodbank.target_agent_id !== role.agentId) {
|
|
5054
5181
|
details.push(`registry entry for ${role.agentId} must advertise bloodbank { gateway_scope: fleet, target_agent_id: ${role.agentId} }`);
|
|
5055
5182
|
}
|
|
@@ -5070,7 +5197,7 @@ function createHermesChecks() {
|
|
|
5070
5197
|
status: details.length === 0 ? "pass" : "fail",
|
|
5071
5198
|
summary: details.length === 0 ? "Fleet registry is in parity" : `${details.length} registry parity issue(s) detected`,
|
|
5072
5199
|
details,
|
|
5073
|
-
fixable:
|
|
5200
|
+
fixable: !malformedRoleGate
|
|
5074
5201
|
};
|
|
5075
5202
|
},
|
|
5076
5203
|
migrate: (ctx, finding) => {
|
|
@@ -5082,6 +5209,19 @@ function createHermesChecks() {
|
|
|
5082
5209
|
return { id: finding.id, title: finding.title, status: "blocked", summary: `registry unreadable at ${registryPath2}`, changedFiles, details };
|
|
5083
5210
|
}
|
|
5084
5211
|
const roles = discoverRoles(ctx.repoRoot);
|
|
5212
|
+
const malformedRoleGates = roles.filter((role) => roleBloodbankEnabled(role) === null);
|
|
5213
|
+
if (malformedRoleGates.length > 0) {
|
|
5214
|
+
return {
|
|
5215
|
+
id: finding.id,
|
|
5216
|
+
title: finding.title,
|
|
5217
|
+
status: "blocked",
|
|
5218
|
+
summary: "Registry parity is blocked by malformed role Bloodbank gates",
|
|
5219
|
+
changedFiles,
|
|
5220
|
+
details: malformedRoleGates.map(
|
|
5221
|
+
(role) => `${relative2(ctx.repoRoot, role.roleYamlPath)} bloodbank.enabled must be the strict YAML boolean true or false`
|
|
5222
|
+
)
|
|
5223
|
+
};
|
|
5224
|
+
}
|
|
5085
5225
|
const missingRoles = roles.filter((role) => !raw.includes(`${role.agentId}:`));
|
|
5086
5226
|
for (const role of missingRoles) {
|
|
5087
5227
|
const updated = upsertRegistryEntry(role, ctx.homeDir, changedFiles, ctx.dryRun);
|
|
@@ -5145,9 +5285,10 @@ function createHermesChecks() {
|
|
|
5145
5285
|
dirty = true;
|
|
5146
5286
|
}
|
|
5147
5287
|
const bloodbank = entry.bloodbank ?? {};
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5288
|
+
const expectedBloodbankEnabled = roleBloodbankEnabled(role) ?? false;
|
|
5289
|
+
if (bloodbank.enabled !== expectedBloodbankEnabled || bloodbank.gateway_scope !== "fleet" || bloodbank.target_agent_id !== role.agentId) {
|
|
5290
|
+
details.push(`normalize fleet bloodbank routing for ${role.agentId} with enabled=${expectedBloodbankEnabled}`);
|
|
5291
|
+
entry.bloodbank = { ...bloodbank, enabled: expectedBloodbankEnabled, gateway_scope: "fleet", target_agent_id: role.agentId };
|
|
5151
5292
|
dirty = true;
|
|
5152
5293
|
}
|
|
5153
5294
|
const systemd = entry.systemd;
|
|
@@ -5307,10 +5448,10 @@ var AgentHooksRecipe = class extends Recipe {
|
|
|
5307
5448
|
printNextSteps() {
|
|
5308
5449
|
console.log("\u{1FA9D} Agent-hooks layer installed!");
|
|
5309
5450
|
console.log(" Next steps:");
|
|
5310
|
-
console.log(" 1. mise run skills
|
|
5311
|
-
console.log(" 2. mise run hooks
|
|
5451
|
+
console.log(" 1. mise run skills:sync # sync .agents/skills.json into local CLI dirs");
|
|
5452
|
+
console.log(" 2. mise run hooks:sync # generate .claude/settings.json + inject codex/kimi/hermes");
|
|
5312
5453
|
console.log(" 3. git add .claude/settings.json .agents/hooks .agents/skills.json && commit (codex/kimi/hermes are per-dev)");
|
|
5313
|
-
console.log(" 4. mise run hindsight
|
|
5454
|
+
console.log(" 4. mise run hindsight:setup # set HINDSIGHT_OP_KEY_REF to your 1Password item first");
|
|
5314
5455
|
console.log(" 5. Optional per-dev hook opt-out: copy .agents/local.example.json -> .agents/local.json");
|
|
5315
5456
|
}
|
|
5316
5457
|
};
|
|
@@ -5662,6 +5803,9 @@ var PromptForAgentConfig = class extends Command {
|
|
|
5662
5803
|
ctx.soulTone ??= "direct";
|
|
5663
5804
|
ctx.modelProvider ??= "";
|
|
5664
5805
|
ctx.modelName ??= "";
|
|
5806
|
+
ctx.modelBaseUrl ??= "";
|
|
5807
|
+
ctx.modelApiMode ??= "";
|
|
5808
|
+
ctx.modelKeyEnv ??= "";
|
|
5665
5809
|
ctx.ticketProvider ??= detectTicketProvider(ctx.targetDir) ?? "plane";
|
|
5666
5810
|
ctx.skipEmail ??= true;
|
|
5667
5811
|
ctx.agentId = deriveAgentId(ctx.targetRepo, ctx.role);
|
|
@@ -5728,7 +5872,17 @@ function resolveVendoredTemplate(name) {
|
|
|
5728
5872
|
var RunCopierTemplate = class extends Command {
|
|
5729
5873
|
async invoke() {
|
|
5730
5874
|
const ctx = this.context;
|
|
5731
|
-
const {
|
|
5875
|
+
const {
|
|
5876
|
+
targetRepo,
|
|
5877
|
+
role,
|
|
5878
|
+
agentPurpose,
|
|
5879
|
+
soulTone,
|
|
5880
|
+
modelProvider,
|
|
5881
|
+
modelName,
|
|
5882
|
+
modelBaseUrl,
|
|
5883
|
+
modelApiMode,
|
|
5884
|
+
modelKeyEnv
|
|
5885
|
+
} = ctx;
|
|
5732
5886
|
const ticketProvider = ctx.ticketProvider ?? "plane";
|
|
5733
5887
|
const profileName = ctx.profileName ?? (targetRepo && role ? deriveProfileName(targetRepo, role) : void 0);
|
|
5734
5888
|
if (!targetRepo || !role) {
|
|
@@ -5795,6 +5949,12 @@ var RunCopierTemplate = class extends Command {
|
|
|
5795
5949
|
"--data",
|
|
5796
5950
|
`model_name=${modelName ?? ""}`,
|
|
5797
5951
|
"--data",
|
|
5952
|
+
`model_base_url=${modelBaseUrl ?? ""}`,
|
|
5953
|
+
"--data",
|
|
5954
|
+
`model_api_mode=${modelApiMode ?? ""}`,
|
|
5955
|
+
"--data",
|
|
5956
|
+
`model_key_env=${modelKeyEnv ?? ""}`,
|
|
5957
|
+
"--data",
|
|
5798
5958
|
`profile_name=${profileName ?? ""}`,
|
|
5799
5959
|
"--data",
|
|
5800
5960
|
`soul_tone=${soulTone ?? "direct"}`,
|
|
@@ -8101,31 +8261,33 @@ ${leaveBlock}`);
|
|
|
8101
8261
|
const appended = [
|
|
8102
8262
|
"",
|
|
8103
8263
|
_WireMiseAgentHooks.MARKER + " (generated \u2014 see .agents/hooks/README.md)",
|
|
8264
|
+
// PJAN-61: task names use the colon namespace form. A colon is not legal
|
|
8265
|
+
// in a BARE toml key, so every header here MUST stay quoted.
|
|
8104
8266
|
"[[watch_files]]",
|
|
8105
8267
|
'patterns = [".agents/skills.json"]',
|
|
8106
|
-
'task = "skills
|
|
8268
|
+
'task = "skills:sync"',
|
|
8107
8269
|
"",
|
|
8108
|
-
|
|
8270
|
+
'[tasks."skills:sync"]',
|
|
8109
8271
|
'description = "Sync skills from manifest to local CLI dirs"',
|
|
8110
8272
|
'run = "sync-skills.py --scope project"',
|
|
8111
8273
|
"",
|
|
8112
8274
|
"[[watch_files]]",
|
|
8113
8275
|
'patterns = [".agents/hooks/hooks.master.json"]',
|
|
8114
|
-
'task = "hooks
|
|
8276
|
+
'task = "hooks:sync"',
|
|
8115
8277
|
"",
|
|
8116
|
-
|
|
8278
|
+
'[tasks."hooks:sync"]',
|
|
8117
8279
|
'description = "Fan out hooks.master.json to each agent CLI (claude/codex/kimi/hermes)"',
|
|
8118
8280
|
`run = "${cr}/.agents/hooks/sync.py --install"`,
|
|
8119
8281
|
"",
|
|
8120
|
-
|
|
8282
|
+
'[tasks."hooks:check"]',
|
|
8121
8283
|
'description = "Drift gate: verify generated hook configs match hooks.master.json"',
|
|
8122
8284
|
`run = "${cr}/.agents/hooks/sync.py --check"`,
|
|
8123
8285
|
"",
|
|
8124
|
-
|
|
8286
|
+
'[tasks."hooks:uninstall"]',
|
|
8125
8287
|
'description = "Remove per-user agent-hook injections (codex/kimi/hermes)"',
|
|
8126
8288
|
`run = "${cr}/.agents/hooks/sync.py --uninstall"`,
|
|
8127
8289
|
"",
|
|
8128
|
-
|
|
8290
|
+
'[tasks."hindsight:setup"]',
|
|
8129
8291
|
`description = "Provision this dev's shared project Hindsight key from 1Password into .env"`,
|
|
8130
8292
|
`run = "${cr}/.mise/scripts/hindsight-setup.sh"`,
|
|
8131
8293
|
"",
|
|
@@ -8942,11 +9104,14 @@ server.registerTool(
|
|
|
8942
9104
|
inputSchema: {
|
|
8943
9105
|
targetDir: z.string(),
|
|
8944
9106
|
targetRepo: z.string().optional(),
|
|
8945
|
-
role: z.enum(["pm", "dev", "review", "ops", "qa"]),
|
|
9107
|
+
role: z.enum(["pm", "director", "dev", "review", "ops", "qa"]),
|
|
8946
9108
|
agentPurpose: z.string().optional(),
|
|
8947
9109
|
soulTone: z.enum(["direct", "playful", "formal", "terse"]).optional(),
|
|
8948
9110
|
modelProvider: z.string().optional(),
|
|
8949
9111
|
modelName: z.string().optional(),
|
|
9112
|
+
modelBaseUrl: z.string().optional(),
|
|
9113
|
+
modelApiMode: z.enum(["", "chat_completions", "codex_responses", "anthropic_messages", "bedrock_converse", "codex_app_server"]).optional(),
|
|
9114
|
+
modelKeyEnv: z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).optional(),
|
|
8950
9115
|
local: z.boolean().optional(),
|
|
8951
9116
|
force: z.boolean().optional(),
|
|
8952
9117
|
dryRun: z.boolean().optional(),
|
|
@@ -8973,6 +9138,9 @@ server.registerTool(
|
|
|
8973
9138
|
soulTone: input.soulTone,
|
|
8974
9139
|
modelProvider: input.modelProvider,
|
|
8975
9140
|
modelName: input.modelName,
|
|
9141
|
+
modelBaseUrl: input.modelBaseUrl,
|
|
9142
|
+
modelApiMode: input.modelApiMode,
|
|
9143
|
+
modelKeyEnv: input.modelKeyEnv,
|
|
8976
9144
|
ticketProvider: input.ticketProvider,
|
|
8977
9145
|
force: input.force ?? false,
|
|
8978
9146
|
dryRun: input.dryRun ?? false,
|