@delorenj/pjangler 1.2.6 → 1.2.9
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 +54 -16
- package/dist/mcp-server.js +66 -17
- package/package.json +1 -1
- package/templates/commonproject/AGENTS.md +3 -3
- package/templates/commonproject/README.md +12 -11
- package/templates/commonproject/copier.yml +24 -8
- package/templates/commonproject/mise.toml +3 -3
- package/templates/commonproject/template/.project.json.jinja +13 -6
- package/templates/hermes-agent/README.md +8 -8
- package/templates/hermes-agent/copier.yml +3 -4
- package/templates/hermes-agent/docs/architecture.md +4 -4
- package/templates/hermes-agent/docs/operations.md +2 -2
- package/templates/hermes-agent/docs/sentinel/README.md +6 -8
- package/templates/hermes-agent/docs/sentinel/architecture.md +2 -1
- package/templates/hermes-agent/docs/sentinel/development.md +13 -12
- package/templates/hermes-agent/docs/sentinel/providers.md +12 -32
- package/templates/hermes-agent/install-local.sh +6 -13
- package/templates/hermes-agent/template/.scripts/42-ticket-provider.sh +14 -37
- package/templates/hermes-agent/template/.scripts/70-systemd.sh +1 -0
- package/templates/hermes-agent/template/.scripts/lib/ticket-provider.sh +6 -2
- package/templates/hermes-agent/template/.scripts/sentinel/docs/autonomous-delegated-review.md +2 -2
- package/templates/hermes-agent/template/.scripts/sentinel/docs/continuous-ticket-orchestration.md +1 -1
- package/templates/hermes-agent/template/.scripts/sentinel.prompt.md.jinja +5 -7
- package/templates/hermes-agent/template/SOUL.md.jinja +8 -7
- package/templates/hermes-agent/template/role.yaml.jinja +3 -6
- package/templates/hermes-agent/template/.scripts/40-plane.sh +0 -51
- package/templates/hermes-agent/template/.scripts/providers/linear.sh +0 -176
package/dist/index.js
CHANGED
|
@@ -727,7 +727,7 @@ import * as p from "@clack/prompts";
|
|
|
727
727
|
function detectTicketProvider(targetDir) {
|
|
728
728
|
try {
|
|
729
729
|
const t = JSON.parse(readFileSync(join4(targetDir, ".project.json"), "utf8"))?.ticket_provider?.type;
|
|
730
|
-
return t === "plane" || t === "
|
|
730
|
+
return t === "plane" || t === "trello" ? t : void 0;
|
|
731
731
|
} catch {
|
|
732
732
|
return void 0;
|
|
733
733
|
}
|
|
@@ -1274,6 +1274,34 @@ function validateProjectRegistry(registry) {
|
|
|
1274
1274
|
}
|
|
1275
1275
|
}
|
|
1276
1276
|
}
|
|
1277
|
+
function normalizeTicketProvider(value) {
|
|
1278
|
+
const type = (value || "plane").trim().toLowerCase();
|
|
1279
|
+
if (type === "plane" || type === "trello") return type;
|
|
1280
|
+
throw new Error(`Unsupported ticket provider: ${value}. Supported providers: plane, trello`);
|
|
1281
|
+
}
|
|
1282
|
+
function buildTicketProviderBlock(input) {
|
|
1283
|
+
const type = normalizeTicketProvider(input.type);
|
|
1284
|
+
const boardId = input.boardId ?? "";
|
|
1285
|
+
if (type === "trello") {
|
|
1286
|
+
return {
|
|
1287
|
+
type,
|
|
1288
|
+
workspace: input.workspace ?? "",
|
|
1289
|
+
identifier: input.identifier,
|
|
1290
|
+
board_id: boardId,
|
|
1291
|
+
board_url: input.boardUrl ?? (boardId ? `https://trello.com/b/${boardId}` : ""),
|
|
1292
|
+
state: "planned"
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
const workspace = input.workspace ?? "33god";
|
|
1296
|
+
return {
|
|
1297
|
+
type,
|
|
1298
|
+
workspace,
|
|
1299
|
+
identifier: input.identifier,
|
|
1300
|
+
board_id: boardId,
|
|
1301
|
+
board_url: input.boardUrl ?? (boardId ? `https://plane.delo.sh/${workspace}/projects/${boardId}/issues/` : ""),
|
|
1302
|
+
state: "planned"
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1277
1305
|
function slugifyProjectName(value) {
|
|
1278
1306
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
1279
1307
|
}
|
|
@@ -1352,14 +1380,13 @@ function planProjectInit(input) {
|
|
|
1352
1380
|
primary_language: input.primaryLanguage ?? "python"
|
|
1353
1381
|
}
|
|
1354
1382
|
},
|
|
1355
|
-
ticket_provider: {
|
|
1383
|
+
ticket_provider: buildTicketProviderBlock({
|
|
1356
1384
|
type: input.ticketProvider ?? "plane",
|
|
1357
|
-
workspace: input.planeWorkspace ?? "33god",
|
|
1358
1385
|
identifier,
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
},
|
|
1386
|
+
boardId: input.boardId ?? input.planeProjectId,
|
|
1387
|
+
boardUrl: input.boardUrl,
|
|
1388
|
+
workspace: input.boardWorkspace ?? input.planeWorkspace
|
|
1389
|
+
}),
|
|
1363
1390
|
agents,
|
|
1364
1391
|
created_at: existing?.created_at ?? now,
|
|
1365
1392
|
updated_at: now
|
|
@@ -1386,6 +1413,9 @@ function planProjectInit(input) {
|
|
|
1386
1413
|
ticketProvider: project.ticket_provider.type,
|
|
1387
1414
|
planeWorkspace: project.ticket_provider.workspace ?? "33god",
|
|
1388
1415
|
planeProjectId: project.ticket_provider.board_id ?? "",
|
|
1416
|
+
ticketWorkspace: project.ticket_provider.workspace ?? "",
|
|
1417
|
+
boardId: project.ticket_provider.board_id ?? "",
|
|
1418
|
+
boardUrl: project.ticket_provider.board_url ?? "",
|
|
1389
1419
|
projectIdentifier: identifier,
|
|
1390
1420
|
primaryLanguage: project.template.commonproject.primary_language,
|
|
1391
1421
|
agentHooksLayer: resolveAgentHooksLayer(input.agentHooksLayer),
|
|
@@ -1395,9 +1425,10 @@ function planProjectInit(input) {
|
|
|
1395
1425
|
actions.push(
|
|
1396
1426
|
{ kind: "project.write-manifest", path: join8(targetDir, ".project.json"), manifest },
|
|
1397
1427
|
{
|
|
1398
|
-
kind: "
|
|
1428
|
+
kind: "ticket-provider.create-or-link",
|
|
1399
1429
|
enabled: live,
|
|
1400
1430
|
live,
|
|
1431
|
+
provider: project.ticket_provider.type,
|
|
1401
1432
|
workspace: project.ticket_provider.workspace ?? "33god",
|
|
1402
1433
|
identifier,
|
|
1403
1434
|
state: live ? "planned" : "planned",
|
|
@@ -1460,8 +1491,8 @@ function executeProjectInitPlan(plan) {
|
|
|
1460
1491
|
}
|
|
1461
1492
|
} else if (action.kind === "registry.upsert") {
|
|
1462
1493
|
pendingRegistryAction = action;
|
|
1463
|
-
} else if (action.kind === "
|
|
1464
|
-
logs.push(action.enabled ? "
|
|
1494
|
+
} else if (action.kind === "ticket-provider.create-or-link") {
|
|
1495
|
+
logs.push(action.enabled ? "ticket-provider.create-or-link requires a live provider integration" : "ticket-provider.create-or-link skipped (requires --live)");
|
|
1465
1496
|
} else if (action.kind === "hermes.provision-agent") {
|
|
1466
1497
|
logs.push(action.enabled ? "hermes.provision-agent planned for the caller to execute" : "hermes.provision-agent skipped");
|
|
1467
1498
|
}
|
|
@@ -1515,7 +1546,7 @@ function formatProjectInitPlan(plan) {
|
|
|
1515
1546
|
lines.push(` ${cyan(glyph.bullet)} ${action.kind}`);
|
|
1516
1547
|
if (action.kind === "copier.copy.commonproject") lines.push(` ${dim(`target: ${action.targetDir}`)}`);
|
|
1517
1548
|
if (action.kind === "project.write-manifest") lines.push(` ${dim(`path: ${action.path}`)}`);
|
|
1518
|
-
if (action.kind === "
|
|
1549
|
+
if (action.kind === "ticket-provider.create-or-link" && action.reason) lines.push(` ${dim(`note: ${action.reason}`)}`);
|
|
1519
1550
|
}
|
|
1520
1551
|
lines.push("");
|
|
1521
1552
|
return lines.join("\n");
|
|
@@ -1578,6 +1609,9 @@ function buildCommonProjectCopierAction(input) {
|
|
|
1578
1609
|
ticket_provider: input.ticketProvider,
|
|
1579
1610
|
plane_workspace: input.planeWorkspace,
|
|
1580
1611
|
plane_project_id: input.planeProjectId ?? "",
|
|
1612
|
+
ticket_workspace: input.ticketWorkspace ?? input.planeWorkspace,
|
|
1613
|
+
board_id: input.boardId ?? input.planeProjectId ?? "",
|
|
1614
|
+
board_url: input.boardUrl ?? "",
|
|
1581
1615
|
project_identifier: input.projectIdentifier,
|
|
1582
1616
|
primary_language: input.primaryLanguage,
|
|
1583
1617
|
agent_hooks_layer: input.agentHooksLayer ?? true ? "true" : "false"
|
|
@@ -3416,7 +3450,7 @@ function projectInitActionLabel(kind) {
|
|
|
3416
3450
|
return "Render CommonProject scaffold";
|
|
3417
3451
|
case "project.write-manifest":
|
|
3418
3452
|
return "Write repo-local .project.json projection";
|
|
3419
|
-
case "
|
|
3453
|
+
case "ticket-provider.create-or-link":
|
|
3420
3454
|
return "Create/link ticket provider project";
|
|
3421
3455
|
case "hermes.provision-agent":
|
|
3422
3456
|
return "Provision Hermes agent";
|
|
@@ -3442,7 +3476,7 @@ function actionNeedsRun(plan, kind, syncMode) {
|
|
|
3442
3476
|
return !existsSync9(action.path) || readFileSync6(action.path, "utf8") !== next;
|
|
3443
3477
|
}
|
|
3444
3478
|
if (kind === "copier.copy.commonproject") return true;
|
|
3445
|
-
if (kind === "
|
|
3479
|
+
if (kind === "ticket-provider.create-or-link") return plan.actions.some((action) => action.kind === kind && action.enabled);
|
|
3446
3480
|
if (kind === "hermes.provision-agent") return plan.actions.some((action) => action.kind === kind && action.enabled);
|
|
3447
3481
|
return true;
|
|
3448
3482
|
}
|
|
@@ -3540,7 +3574,7 @@ async function runRecipeSubsystem(name, options) {
|
|
|
3540
3574
|
}
|
|
3541
3575
|
var program = new Command3();
|
|
3542
3576
|
program.name("pjangler").description("Project subsystem bootstrapper CLI").version(PJANGLER_VERSION);
|
|
3543
|
-
program.command("init").argument("[name]", "Project name to bootstrap (omit inside an existing git repo)").description("Bootstrap a project: registry entry + CommonProject scaffold + .project.json").option("--description <text>", "Project description").option("--target-dir <path>", "Target repo path").option("--source-skill <path>", "Source skill/template provenance path").option("--primary-language <language>", "Primary language for CommonProject rendering", "python").option("--provision-agent", "Plan local Hermes PM agent provisioning").option("--agent-role <role>", "Hermes agent role to plan when --provision-agent is set", "pm").option("--apply", "Write the registry and render the repo scaffold").option("--dry-run", "Preview changes without writing files (default)").option("--live", "Allow live/network/cloud provisioning actions").option("--slug <slug>", "Project registry slug override").option("--identifier <identifier>", "Ticket identifier override").option("--registry <path>", `Registry path override (default: ${projectRegistryPath()})`).option("-f, --force", "Allow replacing an existing registry entry and re-rendering files").option("-y, --yes", "Apply every proposed operation without prompting").option("--no-tui", "Disable interactive prompts").option("--json", "Output machine-parseable JSON").action(async (name, options) => {
|
|
3577
|
+
program.command("init").argument("[name]", "Project name to bootstrap (omit inside an existing git repo)").description("Bootstrap a project: registry entry + CommonProject scaffold + .project.json").option("--description <text>", "Project description").option("--target-dir <path>", "Target repo path").option("--source-skill <path>", "Source skill/template provenance path").option("--primary-language <language>", "Primary language for CommonProject rendering", "python").option("--provision-agent", "Plan local Hermes PM agent provisioning").option("--agent-role <role>", "Hermes agent role to plan when --provision-agent is set", "pm").option("--apply", "Write the registry and render the repo scaffold").option("--dry-run", "Preview changes without writing files (default)").option("--live", "Allow live/network/cloud provisioning actions").option("--slug <slug>", "Project registry slug override").option("--identifier <identifier>", "Ticket identifier override").option("--ticket-provider <type>", "Ticket provider: plane | trello", "plane").option("--board-id <id>", "Board id (Plane project UUID or Trello board id)").option("--board-url <url>", "Board URL override (derived from provider + board-id if omitted)").option("--workspace <name>", "Ticket workspace/org (Plane workspace; blank for Trello)").option("--registry <path>", `Registry path override (default: ${projectRegistryPath()})`).option("-f, --force", "Allow replacing an existing registry entry and re-rendering files").option("-y, --yes", "Apply every proposed operation without prompting").option("--no-tui", "Disable interactive prompts").option("--json", "Output machine-parseable JSON").action(async (name, options) => {
|
|
3544
3578
|
if (name && getRecipeNames().includes(name)) {
|
|
3545
3579
|
if (!options.json) {
|
|
3546
3580
|
console.error(`${yellow(glyph.warn)} ${dim(`"pjangler init ${name}" is deprecated \u2014 use "pjangler add ${name}". Forwarding\u2026`)}`);
|
|
@@ -3569,7 +3603,7 @@ program.command("list").description("List available subsystems").action(() => {
|
|
|
3569
3603
|
console.log("");
|
|
3570
3604
|
});
|
|
3571
3605
|
var projectCmd = program.command("project").description("Manage the pjangler project registry");
|
|
3572
|
-
projectCmd.command("init").argument("[name]", "Project display name").description("Plan or apply a registry-backed CommonProject initialization or legacy repo sync").option("--description <text>", "Project description").option("--target-dir <path>", "Target repo path").option("--source-skill <path>", "Source skill/template provenance path").option("--primary-language <language>", "Primary language for CommonProject rendering", "python").option("--provision-agent", "Plan local Hermes PM agent provisioning").option("--agent-role <role>", "Hermes agent role to plan when --provision-agent is set", "pm").option("--apply", "Write the registry and render the repo scaffold").option("--dry-run", "Preview changes without writing files (default)").option("--live", "Allow live/network/cloud provisioning actions").option("--slug <slug>", "Project registry slug override").option("--identifier <identifier>", "Ticket identifier override").option("--registry <path>", `Registry path override (default: ${projectRegistryPath()})`).option("-f, --force", "Allow replacing an existing registry entry and re-rendering files").option("-y, --yes", "Apply every proposed operation without prompting").option("--no-tui", "Disable interactive prompts").option("--json", "Output machine-parseable JSON").action((name, options) => {
|
|
3606
|
+
projectCmd.command("init").argument("[name]", "Project display name").description("Plan or apply a registry-backed CommonProject initialization or legacy repo sync").option("--description <text>", "Project description").option("--target-dir <path>", "Target repo path").option("--source-skill <path>", "Source skill/template provenance path").option("--primary-language <language>", "Primary language for CommonProject rendering", "python").option("--provision-agent", "Plan local Hermes PM agent provisioning").option("--agent-role <role>", "Hermes agent role to plan when --provision-agent is set", "pm").option("--apply", "Write the registry and render the repo scaffold").option("--dry-run", "Preview changes without writing files (default)").option("--live", "Allow live/network/cloud provisioning actions").option("--slug <slug>", "Project registry slug override").option("--identifier <identifier>", "Ticket identifier override").option("--ticket-provider <type>", "Ticket provider: plane | trello", "plane").option("--board-id <id>", "Board id (Plane project UUID or Trello board id)").option("--board-url <url>", "Board URL override (derived from provider + board-id if omitted)").option("--workspace <name>", "Ticket workspace/org (Plane workspace; blank for Trello)").option("--registry <path>", `Registry path override (default: ${projectRegistryPath()})`).option("-f, --force", "Allow replacing an existing registry entry and re-rendering files").option("-y, --yes", "Apply every proposed operation without prompting").option("--no-tui", "Disable interactive prompts").option("--json", "Output machine-parseable JSON").action((name, options) => {
|
|
3573
3607
|
if (!options.json) console.error(`${yellow(glyph.warn)} ${dim('"pjangler project init" is deprecated \u2014 use "pjangler init".')}`);
|
|
3574
3608
|
return runProjectInit(name, options);
|
|
3575
3609
|
});
|
|
@@ -3590,6 +3624,10 @@ async function runProjectInit(name, options) {
|
|
|
3590
3624
|
live: options.live ?? false,
|
|
3591
3625
|
projectSlug: target.slug,
|
|
3592
3626
|
projectIdentifier: target.identifier,
|
|
3627
|
+
ticketProvider: options.ticketProvider,
|
|
3628
|
+
boardId: options.boardId,
|
|
3629
|
+
boardUrl: options.boardUrl,
|
|
3630
|
+
boardWorkspace: options.workspace,
|
|
3593
3631
|
registryPath: options.registry,
|
|
3594
3632
|
force: options.force ?? false,
|
|
3595
3633
|
overwrite: options.force ?? false,
|
|
@@ -3886,7 +3924,7 @@ program.command("migrate").argument("[rule-id]", "Rule ID to migrate (omit to op
|
|
|
3886
3924
|
process.exit(1);
|
|
3887
3925
|
}
|
|
3888
3926
|
});
|
|
3889
|
-
program.command("hermes-agent").alias("hermes").description("Provision the PM agent for the current repo (defaults everything; only asks about Telegram)").option("-y, --yes", "Non-interactive: accept all defaults (also skips the Telegram prompt)").option("--target-repo <name>", "Target repo name (default: basename of cwd)").option("--role <role>", "Agent role override (default: pm \u2014 the only role in the fleet)").option("--purpose <text>", 'One-line agent purpose (default: "pm agent for <repo>")').option(`--tone <tone>`, `Personality tone (default: direct; ${SOUL_TONES.join(" | ")})`).option("--model-provider <name>", 'Inference provider override ("" = inherit shared default profile)').option("--model-name <name>", 'Model name override ("" = inherit shared default profile)').option("--skip-telegram", "Skip the Telegram wire-up (no BotFather prompt)").option("--email", "Also provision the delo.sh email address (off by default; never prompted)").option("--skip-runtime-repo", "Skip creating the per-agent runtime GH repo").option("--skip-plane", "Skip creating the
|
|
3927
|
+
program.command("hermes-agent").alias("hermes").description("Provision the PM agent for the current repo (defaults everything; only asks about Telegram)").option("-y, --yes", "Non-interactive: accept all defaults (also skips the Telegram prompt)").option("--target-repo <name>", "Target repo name (default: basename of cwd)").option("--role <role>", "Agent role override (default: pm \u2014 the only role in the fleet)").option("--purpose <text>", 'One-line agent purpose (default: "pm agent for <repo>")').option(`--tone <tone>`, `Personality tone (default: direct; ${SOUL_TONES.join(" | ")})`).option("--model-provider <name>", 'Inference provider override ("" = inherit shared default profile)').option("--model-name <name>", 'Model name override ("" = inherit shared default profile)').option("--skip-telegram", "Skip the Telegram wire-up (no BotFather prompt)").option("--email", "Also provision the delo.sh email address (off by default; never prompted)").option("--skip-runtime-repo", "Skip creating the per-agent runtime GH repo").option("--skip-plane", "Skip creating or linking the ticket board").option("--skip-bloodbank", "Skip installing the Bloodbank NATS consumer").option("--skip-systemd", "Skip installing systemd --user units").option("--local", "Local-only: skip runtime repo, ticket-board creation, Bloodbank, and systemd (safe for laptops/macOS/non-technical operators)").option("--force-config", "Regenerate ~/.config/hermes-agent-template/config.toml even if it exists").option("--dry-run", "Preview what would run; don't execute copier").option("-f, --force", "Re-render even if agents/hermes/<role>/role.yaml already exists").action(async (options) => {
|
|
3890
3928
|
const isDarwin = process.platform === "darwin";
|
|
3891
3929
|
const local = options.local ?? false;
|
|
3892
3930
|
const context = {
|
package/dist/mcp-server.js
CHANGED
|
@@ -713,7 +713,7 @@ function deriveProfileName(repo, role) {
|
|
|
713
713
|
function detectTicketProvider(targetDir) {
|
|
714
714
|
try {
|
|
715
715
|
const t = JSON.parse(readFileSync(join4(targetDir, ".project.json"), "utf8"))?.ticket_provider?.type;
|
|
716
|
-
return t === "plane" || t === "
|
|
716
|
+
return t === "plane" || t === "trello" ? t : void 0;
|
|
717
717
|
} catch {
|
|
718
718
|
return void 0;
|
|
719
719
|
}
|
|
@@ -1260,6 +1260,34 @@ function validateProjectRegistry(registry) {
|
|
|
1260
1260
|
}
|
|
1261
1261
|
}
|
|
1262
1262
|
}
|
|
1263
|
+
function normalizeTicketProvider(value) {
|
|
1264
|
+
const type = (value || "plane").trim().toLowerCase();
|
|
1265
|
+
if (type === "plane" || type === "trello") return type;
|
|
1266
|
+
throw new Error(`Unsupported ticket provider: ${value}. Supported providers: plane, trello`);
|
|
1267
|
+
}
|
|
1268
|
+
function buildTicketProviderBlock(input) {
|
|
1269
|
+
const type = normalizeTicketProvider(input.type);
|
|
1270
|
+
const boardId = input.boardId ?? "";
|
|
1271
|
+
if (type === "trello") {
|
|
1272
|
+
return {
|
|
1273
|
+
type,
|
|
1274
|
+
workspace: input.workspace ?? "",
|
|
1275
|
+
identifier: input.identifier,
|
|
1276
|
+
board_id: boardId,
|
|
1277
|
+
board_url: input.boardUrl ?? (boardId ? `https://trello.com/b/${boardId}` : ""),
|
|
1278
|
+
state: "planned"
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
const workspace = input.workspace ?? "33god";
|
|
1282
|
+
return {
|
|
1283
|
+
type,
|
|
1284
|
+
workspace,
|
|
1285
|
+
identifier: input.identifier,
|
|
1286
|
+
board_id: boardId,
|
|
1287
|
+
board_url: input.boardUrl ?? (boardId ? `https://plane.delo.sh/${workspace}/projects/${boardId}/issues/` : ""),
|
|
1288
|
+
state: "planned"
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1263
1291
|
function slugifyProjectName(value) {
|
|
1264
1292
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
1265
1293
|
}
|
|
@@ -1338,14 +1366,13 @@ function planProjectInit(input) {
|
|
|
1338
1366
|
primary_language: input.primaryLanguage ?? "python"
|
|
1339
1367
|
}
|
|
1340
1368
|
},
|
|
1341
|
-
ticket_provider: {
|
|
1369
|
+
ticket_provider: buildTicketProviderBlock({
|
|
1342
1370
|
type: input.ticketProvider ?? "plane",
|
|
1343
|
-
workspace: input.planeWorkspace ?? "33god",
|
|
1344
1371
|
identifier,
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
},
|
|
1372
|
+
boardId: input.boardId ?? input.planeProjectId,
|
|
1373
|
+
boardUrl: input.boardUrl,
|
|
1374
|
+
workspace: input.boardWorkspace ?? input.planeWorkspace
|
|
1375
|
+
}),
|
|
1349
1376
|
agents,
|
|
1350
1377
|
created_at: existing?.created_at ?? now,
|
|
1351
1378
|
updated_at: now
|
|
@@ -1372,6 +1399,9 @@ function planProjectInit(input) {
|
|
|
1372
1399
|
ticketProvider: project.ticket_provider.type,
|
|
1373
1400
|
planeWorkspace: project.ticket_provider.workspace ?? "33god",
|
|
1374
1401
|
planeProjectId: project.ticket_provider.board_id ?? "",
|
|
1402
|
+
ticketWorkspace: project.ticket_provider.workspace ?? "",
|
|
1403
|
+
boardId: project.ticket_provider.board_id ?? "",
|
|
1404
|
+
boardUrl: project.ticket_provider.board_url ?? "",
|
|
1375
1405
|
projectIdentifier: identifier,
|
|
1376
1406
|
primaryLanguage: project.template.commonproject.primary_language,
|
|
1377
1407
|
agentHooksLayer: resolveAgentHooksLayer(input.agentHooksLayer),
|
|
@@ -1381,9 +1411,10 @@ function planProjectInit(input) {
|
|
|
1381
1411
|
actions.push(
|
|
1382
1412
|
{ kind: "project.write-manifest", path: join8(targetDir, ".project.json"), manifest },
|
|
1383
1413
|
{
|
|
1384
|
-
kind: "
|
|
1414
|
+
kind: "ticket-provider.create-or-link",
|
|
1385
1415
|
enabled: live,
|
|
1386
1416
|
live,
|
|
1417
|
+
provider: project.ticket_provider.type,
|
|
1387
1418
|
workspace: project.ticket_provider.workspace ?? "33god",
|
|
1388
1419
|
identifier,
|
|
1389
1420
|
state: live ? "planned" : "planned",
|
|
@@ -1446,8 +1477,8 @@ function executeProjectInitPlan(plan) {
|
|
|
1446
1477
|
}
|
|
1447
1478
|
} else if (action.kind === "registry.upsert") {
|
|
1448
1479
|
pendingRegistryAction = action;
|
|
1449
|
-
} else if (action.kind === "
|
|
1450
|
-
logs.push(action.enabled ? "
|
|
1480
|
+
} else if (action.kind === "ticket-provider.create-or-link") {
|
|
1481
|
+
logs.push(action.enabled ? "ticket-provider.create-or-link requires a live provider integration" : "ticket-provider.create-or-link skipped (requires --live)");
|
|
1451
1482
|
} else if (action.kind === "hermes.provision-agent") {
|
|
1452
1483
|
logs.push(action.enabled ? "hermes.provision-agent planned for the caller to execute" : "hermes.provision-agent skipped");
|
|
1453
1484
|
}
|
|
@@ -1502,6 +1533,9 @@ function buildCommonProjectCopierAction(input) {
|
|
|
1502
1533
|
ticket_provider: input.ticketProvider,
|
|
1503
1534
|
plane_workspace: input.planeWorkspace,
|
|
1504
1535
|
plane_project_id: input.planeProjectId ?? "",
|
|
1536
|
+
ticket_workspace: input.ticketWorkspace ?? input.planeWorkspace,
|
|
1537
|
+
board_id: input.boardId ?? input.planeProjectId ?? "",
|
|
1538
|
+
board_url: input.boardUrl ?? "",
|
|
1505
1539
|
project_identifier: input.projectIdentifier,
|
|
1506
1540
|
primary_language: input.primaryLanguage,
|
|
1507
1541
|
agent_hooks_layer: input.agentHooksLayer ?? true ? "true" : "false"
|
|
@@ -3221,7 +3255,7 @@ var server = new McpServer({
|
|
|
3221
3255
|
name: "pjangler-mcp",
|
|
3222
3256
|
version: PJANGLER_VERSION
|
|
3223
3257
|
});
|
|
3224
|
-
var TICKET_PROVIDER_SCHEMA = z.enum(["plane", "
|
|
3258
|
+
var TICKET_PROVIDER_SCHEMA = z.enum(["plane", "trello"]);
|
|
3225
3259
|
function resolveTargetDir(targetDir) {
|
|
3226
3260
|
const dir = resolve3(targetDir ?? process.cwd());
|
|
3227
3261
|
if (!existsSync9(dir)) {
|
|
@@ -3412,6 +3446,9 @@ server.registerTool(
|
|
|
3412
3446
|
projectDescription: z.string().optional(),
|
|
3413
3447
|
projectSlug: z.string().optional(),
|
|
3414
3448
|
ticketProvider: TICKET_PROVIDER_SCHEMA.optional(),
|
|
3449
|
+
boardId: z.string().optional(),
|
|
3450
|
+
boardUrl: z.string().optional(),
|
|
3451
|
+
workspace: z.string().optional(),
|
|
3415
3452
|
planeWorkspace: z.string().optional(),
|
|
3416
3453
|
planeProjectId: z.string().optional(),
|
|
3417
3454
|
projectIdentifier: z.string().optional(),
|
|
@@ -3440,9 +3477,10 @@ server.registerTool(
|
|
|
3440
3477
|
const dryRun = input.dryRun ?? true;
|
|
3441
3478
|
const local = input.local ?? true;
|
|
3442
3479
|
const skipPlane = input.skipPlane ?? true;
|
|
3443
|
-
const
|
|
3444
|
-
|
|
3445
|
-
|
|
3480
|
+
const ticketProvider = input.ticketProvider ?? "plane";
|
|
3481
|
+
const boardId = input.boardId ?? input.planeProjectId ?? "";
|
|
3482
|
+
if (!skipPlane && ticketProvider === "plane" && !boardId) {
|
|
3483
|
+
throw new Error("boardId or planeProjectId is required when skipPlane=false for Plane; keep skipPlane=true for safe local bootstrap");
|
|
3446
3484
|
}
|
|
3447
3485
|
if (!dryRun && existsSync9(targetDir) && !overwrite) throw new Error(`Target already exists: ${targetDir} (set force/overwrite=true to re-render)`);
|
|
3448
3486
|
const plan = planProjectInit({
|
|
@@ -3458,9 +3496,12 @@ server.registerTool(
|
|
|
3458
3496
|
live: input.live ?? false,
|
|
3459
3497
|
registryPath: input.registryPath,
|
|
3460
3498
|
projectIdentifier: input.projectIdentifier ?? projectSlug.slice(0, 4).toUpperCase(),
|
|
3461
|
-
ticketProvider
|
|
3499
|
+
ticketProvider,
|
|
3500
|
+
boardId,
|
|
3501
|
+
boardUrl: input.boardUrl,
|
|
3502
|
+
boardWorkspace: input.workspace ?? input.planeWorkspace,
|
|
3462
3503
|
planeWorkspace: input.planeWorkspace ?? "33god",
|
|
3463
|
-
planeProjectId,
|
|
3504
|
+
planeProjectId: input.planeProjectId,
|
|
3464
3505
|
pjanglerRoot,
|
|
3465
3506
|
overwrite
|
|
3466
3507
|
});
|
|
@@ -3512,6 +3553,10 @@ server.registerTool(
|
|
|
3512
3553
|
live: z.boolean().optional(),
|
|
3513
3554
|
slug: z.string().optional(),
|
|
3514
3555
|
identifier: z.string().optional(),
|
|
3556
|
+
ticketProvider: TICKET_PROVIDER_SCHEMA.optional(),
|
|
3557
|
+
boardId: z.string().optional(),
|
|
3558
|
+
boardUrl: z.string().optional(),
|
|
3559
|
+
workspace: z.string().optional(),
|
|
3515
3560
|
registryPath: z.string().optional(),
|
|
3516
3561
|
force: z.boolean().optional()
|
|
3517
3562
|
}
|
|
@@ -3530,6 +3575,10 @@ server.registerTool(
|
|
|
3530
3575
|
live: input.live ?? false,
|
|
3531
3576
|
projectSlug: input.slug,
|
|
3532
3577
|
projectIdentifier: input.identifier,
|
|
3578
|
+
ticketProvider: input.ticketProvider,
|
|
3579
|
+
boardId: input.boardId,
|
|
3580
|
+
boardUrl: input.boardUrl,
|
|
3581
|
+
boardWorkspace: input.workspace,
|
|
3533
3582
|
registryPath: input.registryPath,
|
|
3534
3583
|
force: input.force ?? false,
|
|
3535
3584
|
overwrite: input.force ?? false
|
|
@@ -3637,7 +3686,7 @@ server.registerTool(
|
|
|
3637
3686
|
"pjangler_deploy_hermes_agent",
|
|
3638
3687
|
{
|
|
3639
3688
|
title: "Deploy Hermes agent",
|
|
3640
|
-
description: "Provision a Hermes agent role for @33god-projects. For safe MCP use local=true defaults skip runtime repo,
|
|
3689
|
+
description: "Provision a Hermes agent role for @33god-projects. For safe MCP use local=true defaults skip runtime repo, ticket-board creation, Bloodbank, and systemd; opt out with local=false plus explicit skip flags.",
|
|
3641
3690
|
inputSchema: {
|
|
3642
3691
|
targetDir: z.string(),
|
|
3643
3692
|
targetRepo: z.string().optional(),
|
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@ CommonProject/
|
|
|
20
20
|
│ ├── .agentvibes/ # AgentVibes config
|
|
21
21
|
│ ├── .mise/tasks/ # File-based mise tasks
|
|
22
22
|
│ ├── .scripts/ # Post-generation utilities
|
|
23
|
-
│ │ └── setup-plane.py # Creates
|
|
23
|
+
│ │ └── setup-plane.py # Creates/links ticket provider board + ticket_provider block in .project.json
|
|
24
24
|
│ ├── AGENTS.md.jinja # Generated project's agent SSOT
|
|
25
25
|
│ ├── CLAUDE.md # Symlink → AGENTS.md
|
|
26
26
|
│ ├── GEMINI.md # Symlink → AGENTS.md
|
|
@@ -46,7 +46,7 @@ Root-level files describe the template itself. Files in `template/` are what Cop
|
|
|
46
46
|
Only two questions asked: `project_name` and `project_description`. Everything else is derived or automated:
|
|
47
47
|
- `project_slug` derived from project_name
|
|
48
48
|
- `user_name` / `user_skill_level` hardcoded for BMAD config
|
|
49
|
-
-
|
|
49
|
+
- Ticket board created or linked via post-generation task
|
|
50
50
|
- .gitignore copied from ~/.config/git/ignore
|
|
51
51
|
- git init + initial commit run automatically
|
|
52
52
|
|
|
@@ -55,7 +55,7 @@ Only two questions asked: `project_name` and `project_description`. Everything e
|
|
|
55
55
|
After rendering, Copier automatically:
|
|
56
56
|
1. Copies .gitignore from ~/.config/git/ignore
|
|
57
57
|
2. Makes scripts executable
|
|
58
|
-
3. Runs setup-plane.py (creates
|
|
58
|
+
3. Runs setup-plane.py (creates/links the ticket board, writes the ticket_provider block in .project.json)
|
|
59
59
|
4. Runs git init + git add -A + git commit
|
|
60
60
|
|
|
61
61
|
### BMAD System
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# CommonProject
|
|
2
2
|
|
|
3
|
-
Copier template for creating new 33GOD ecosystem components with BMAD methodology,
|
|
3
|
+
Copier template for creating new 33GOD ecosystem components with BMAD methodology, ticket-provider enforcement, and multi-agent development tooling.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Language Support**: Python, TypeScript, Rust
|
|
8
8
|
- **Event-Driven Architecture**: Optional [Bloodbank](https://github.com/delorenj/Bloodbank) integration
|
|
9
|
-
- **
|
|
10
|
-
- **Ticket Enforcement**: Git hooks enforce
|
|
9
|
+
- **Ticket Provider Integration**: `.project.json` records Plane or Trello board bindings
|
|
10
|
+
- **Ticket Enforcement**: Git hooks enforce configured ticket-provider requirements
|
|
11
11
|
- [TODO] **BMAD Integration**: Full BMAD methodology structure by auto running `npx bmad-method@latest install`
|
|
12
12
|
- [TODO] Multiple-choice selection for different workflows: BMAD, GSD, GoogleAgentSkills, None.
|
|
13
13
|
- **Containerization**: Optional Docker + Docker Compose
|
|
@@ -25,7 +25,7 @@ uv tool install copier
|
|
|
25
25
|
# Or with pip
|
|
26
26
|
pip install copier
|
|
27
27
|
|
|
28
|
-
# Set up
|
|
28
|
+
# Set up a provider key if you want automated board creation/linking
|
|
29
29
|
export PLANE_API_KEY="your-plane-api-key"
|
|
30
30
|
OR
|
|
31
31
|
[TODO] export PLANE_API_KEY=op://VaultName/Key/Field
|
|
@@ -36,14 +36,14 @@ OR
|
|
|
36
36
|
|
|
37
37
|
### Option 1: Automated Init (Recommended)
|
|
38
38
|
|
|
39
|
-
[TODO] One command handles everything:
|
|
39
|
+
[TODO] One command handles everything: ticket board creation/linking + Copier template:
|
|
40
40
|
|
|
41
41
|
> TASK - Implement the following:
|
|
42
42
|
> Currently, I have to run `copier copy --trust ~/code CommonProject .` globally, or `mise run init-project` while in the CommonProject source dir - but this is not ideal.
|
|
43
43
|
> I would rather run `commonProject .` globally.
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
# Interactive wizard - creates
|
|
46
|
+
# Interactive wizard - creates/links the configured ticket board automatically
|
|
47
47
|
mise run init-project
|
|
48
48
|
|
|
49
49
|
# Or non-interactive (uses defaults)
|
|
@@ -56,7 +56,7 @@ mise run init-project-non-interactive
|
|
|
56
56
|
The wizard will:
|
|
57
57
|
|
|
58
58
|
1. Ask for project details (name, description, type)
|
|
59
|
-
2. Create the
|
|
59
|
+
2. Create or link the configured ticket board
|
|
60
60
|
3. Run Copier with all answers pre-filled
|
|
61
61
|
4. Output next steps
|
|
62
62
|
|
|
@@ -83,9 +83,10 @@ The template asks for:
|
|
|
83
83
|
- **Project Slug**: Directory/package name (auto-generated from name)
|
|
84
84
|
- **Description**: One-sentence project description
|
|
85
85
|
|
|
86
|
-
###
|
|
86
|
+
### Ticket Provider Integration
|
|
87
87
|
|
|
88
|
-
- **
|
|
88
|
+
- **Provider**: Plane or Trello
|
|
89
|
+
- **Workspace**: Provider workspace slug when applicable (Plane default: 33god)
|
|
89
90
|
- **Project Name**
|
|
90
91
|
- **Project Identifier**: 2+ character ticket prefix (e.g., HOLO, VERN)
|
|
91
92
|
|
|
@@ -110,10 +111,10 @@ The template asks for:
|
|
|
110
111
|
git commit -m "Initial commit from template"
|
|
111
112
|
```
|
|
112
113
|
|
|
113
|
-
3. **Create
|
|
114
|
+
3. **Create or Claim Ticket**
|
|
114
115
|
|
|
115
116
|
```bash
|
|
116
|
-
# Create
|
|
117
|
+
# Create or claim a ticket in the configured provider
|
|
117
118
|
# Move to "In Progress"
|
|
118
119
|
git checkout -b <IDENTIFIER>-123-initial-setup
|
|
119
120
|
```
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
#
|
|
4
4
|
# Generic base project template. Creates a project skeleton with:
|
|
5
5
|
# - BMAD methodology (pre-initialized)
|
|
6
|
-
# -
|
|
6
|
+
# - Ticket-provider metadata (Plane or Trello)
|
|
7
7
|
# - mise environment management
|
|
8
8
|
# - CLI coder configs (Claude, Codex, Gemini, etc.)
|
|
9
9
|
|
|
@@ -30,16 +30,15 @@ project_description:
|
|
|
30
30
|
help: One-line description
|
|
31
31
|
default: ""
|
|
32
32
|
|
|
33
|
-
# Ticket board provider for this project.
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
# the
|
|
33
|
+
# Ticket board provider for this project. Plane boards can be created before
|
|
34
|
+
# Copier runs; Trello boards are linked by id/url. The board identity is
|
|
35
|
+
# persisted into .project.json so it is the single source of truth shared with
|
|
36
|
+
# the hermes-agent template's agents.
|
|
37
37
|
ticket_provider:
|
|
38
38
|
type: str
|
|
39
39
|
help: Ticket board provider this project's board lives in
|
|
40
40
|
default: plane
|
|
41
41
|
choices:
|
|
42
|
-
Linear: linear
|
|
43
42
|
Plane: plane
|
|
44
43
|
Trello: trello
|
|
45
44
|
|
|
@@ -50,8 +49,7 @@ project_slug:
|
|
|
50
49
|
default: "{{ project_name | lower | replace(' ', '-') | replace('_', '-') }}"
|
|
51
50
|
when: false
|
|
52
51
|
|
|
53
|
-
#
|
|
54
|
-
# created. Defaults keep a bare `copier copy` rendering valid (empty binding).
|
|
52
|
+
# Back-compat aliases kept for existing pjangler/CommonProject callers.
|
|
55
53
|
plane_workspace:
|
|
56
54
|
type: str
|
|
57
55
|
default: "33god"
|
|
@@ -72,6 +70,24 @@ plane_base:
|
|
|
72
70
|
default: "https://plane.delo.sh"
|
|
73
71
|
when: false
|
|
74
72
|
|
|
73
|
+
# Provider-agnostic board binding — supplied by pjangler's --data values after
|
|
74
|
+
# the board is created or linked. Defaults keep a bare `copier copy` rendering
|
|
75
|
+
# valid, and fall back to the legacy plane_* aliases above.
|
|
76
|
+
board_id:
|
|
77
|
+
type: str
|
|
78
|
+
default: "{{ plane_project_id }}"
|
|
79
|
+
when: false
|
|
80
|
+
|
|
81
|
+
board_url:
|
|
82
|
+
type: str
|
|
83
|
+
default: ""
|
|
84
|
+
when: false
|
|
85
|
+
|
|
86
|
+
ticket_workspace:
|
|
87
|
+
type: str
|
|
88
|
+
default: "{{ plane_workspace if ticket_provider == 'plane' else '' }}"
|
|
89
|
+
when: false
|
|
90
|
+
|
|
75
91
|
# Drives conditional blocks in rendered files (e.g. _.python.venv in mise.toml).
|
|
76
92
|
# Not asked interactively; init-project.sh can override via --data-file.
|
|
77
93
|
primary_language:
|
|
@@ -17,11 +17,11 @@ patterns = ["AGENTS.md"]
|
|
|
17
17
|
task = "link-agentfiles"
|
|
18
18
|
|
|
19
19
|
[tasks]
|
|
20
|
-
# Project initialization - creates
|
|
21
|
-
init-project = { run = ".mise/scripts/init-project.sh", description = "Initialize new 33GOD project with
|
|
20
|
+
# Project initialization - creates/links ticket board + runs Copier
|
|
21
|
+
init-project = { run = ".mise/scripts/init-project.sh", description = "Initialize new 33GOD project with ticket provider + Copier" }
|
|
22
22
|
init-project-non-interactive = { run = ".mise/scripts/init-project.sh --non-interactive", description = "Non-interactive project init" }
|
|
23
23
|
|
|
24
|
-
# Direct Plane project creation
|
|
24
|
+
# Direct Plane project creation helper
|
|
25
25
|
create-plane-project = { run = ".mise/scripts/create-plane-project.sh", description = "Create a Plane project and output ID" }
|
|
26
26
|
|
|
27
27
|
# Template testing
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
{%
|
|
2
|
-
{% set
|
|
1
|
+
{% set ticket_board_id = board_id | default(plane_project_id) -%}
|
|
2
|
+
{% set ticket_board_url = board_url | default('') -%}
|
|
3
|
+
{% set workspace = ticket_workspace | default(plane_workspace if ticket_provider == 'plane' else '') -%}
|
|
4
|
+
{% if ticket_board_url -%}
|
|
5
|
+
{% set resolved_board_url = ticket_board_url -%}
|
|
6
|
+
{% elif ticket_provider == 'plane' and ticket_board_id -%}
|
|
7
|
+
{% set resolved_board_url = plane_base ~ '/' ~ workspace ~ '/projects/' ~ ticket_board_id ~ '/issues/' -%}
|
|
8
|
+
{% elif ticket_provider == 'trello' and ticket_board_id -%}
|
|
9
|
+
{% set resolved_board_url = 'https://trello.com/b/' ~ ticket_board_id -%}
|
|
3
10
|
{% else -%}
|
|
4
|
-
{% set
|
|
11
|
+
{% set resolved_board_url = '' -%}
|
|
5
12
|
{% endif -%}
|
|
6
13
|
{
|
|
7
14
|
"project_name": {{ project_name | tojson }},
|
|
@@ -10,10 +17,10 @@
|
|
|
10
17
|
"repo_path": "",
|
|
11
18
|
"ticket_provider": {
|
|
12
19
|
"type": {{ ticket_provider | tojson }},
|
|
13
|
-
"workspace": {{
|
|
20
|
+
"workspace": {{ workspace | tojson }},
|
|
14
21
|
"identifier": {{ project_identifier | tojson }},
|
|
15
|
-
"board_id": {{
|
|
16
|
-
"board_url": {{
|
|
22
|
+
"board_id": {{ ticket_board_id | tojson }},
|
|
23
|
+
"board_url": {{ resolved_board_url | tojson }}
|
|
17
24
|
},
|
|
18
25
|
"agents": {}
|
|
19
26
|
}
|
|
@@ -11,7 +11,7 @@ memory/state checkpointing.
|
|
|
11
11
|
| What it scaffolds | A new top-level project | An agent role inside an existing project |
|
|
12
12
|
| Copier target | `./my-new-project/` | `./agents/hermes/<role>/` |
|
|
13
13
|
| Asks | project name + description | role + purpose + tone |
|
|
14
|
-
| Post-gen artifacts |
|
|
14
|
+
| Post-gen artifacts | Ticket board binding, bmad init, mise tasks | Ticket board binding, Telegram bot wiring, agent-hm-* runtime repo, systemd units |
|
|
15
15
|
|
|
16
16
|
CommonProject runs first to create the umbrella project. hermes-agent-template
|
|
17
17
|
runs second (for each agent role you want) to drop agents into it.
|
|
@@ -20,8 +20,8 @@ runs second (for each agent role you want) to drop agents into it.
|
|
|
20
20
|
|
|
21
21
|
The template provisions a single Hermes role per invocation. The `pm` role
|
|
22
22
|
handles project management and triage, and also runs the continuous ticket
|
|
23
|
-
sentinel out-of-band: a provider-agnostic board-reconciliation pass (
|
|
24
|
-
|
|
23
|
+
sentinel out-of-band: a provider-agnostic board-reconciliation pass (Plane or
|
|
24
|
+
Trello) with an autonomous adversarial review (act, do not wait). The
|
|
25
25
|
sentinel runs as the PM's **heartbeat** — a fused systemd timer tick that does
|
|
26
26
|
the reconciliation pass and then a gated runtime checkpoint. (There is no
|
|
27
27
|
separate `scrum-master` role; its duties folded into the PM heartbeat.)
|
|
@@ -47,7 +47,7 @@ The template will:
|
|
|
47
47
|
5. Populate it with the runtime scaffold (config.yaml, SOUL.md, memories, consumer.py)
|
|
48
48
|
6. Add it as a git submodule at `agents/hermes/pm/runtime/` (== HERMES_HOME)
|
|
49
49
|
7. Prompt for a BotFather token, store it in `runtime/.env`
|
|
50
|
-
8. Create
|
|
50
|
+
8. Create or bind the configured ticket board
|
|
51
51
|
9. Install systemd `--user` units: gateway, consumer, heartbeat timer (reconcile + checkpoint)
|
|
52
52
|
10. Append the agent to `~/.hermes/agents-registry.yaml`
|
|
53
53
|
|
|
@@ -138,14 +138,14 @@ to force re-run:
|
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
140
|
cd agents/hermes/pm
|
|
141
|
-
rm .scripts/.done-
|
|
142
|
-
|
|
141
|
+
rm .scripts/.done-42-ticket-provider
|
|
142
|
+
SKIP_SYSTEMD=1 ./.scripts/42-ticket-provider.sh
|
|
143
143
|
```
|
|
144
144
|
|
|
145
145
|
## Retiring an agent
|
|
146
146
|
|
|
147
147
|
(TODO: ship `retire.sh` in v1.1)
|
|
148
148
|
|
|
149
|
-
Manual: stop systemd units, `hermes profile delete`, archive the
|
|
150
|
-
|
|
149
|
+
Manual: stop systemd units, `hermes profile delete`, archive the ticket board
|
|
150
|
+
if the provider supports archival, `/deletebot` in BotFather, archive the
|
|
151
151
|
`agent-hm-*` runtime repo, remove the registry entry.
|