@starlein/paperclip-plugin-company-wizard 0.4.5 → 0.4.7

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.
Files changed (71) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +7 -5
  3. package/dist/manifest.js +4 -9
  4. package/dist/manifest.js.map +2 -2
  5. package/dist/ui/index.css +82 -0
  6. package/dist/ui/index.css.map +2 -2
  7. package/dist/ui/index.js +423 -139
  8. package/dist/ui/index.js.map +4 -4
  9. package/dist/worker.js +589 -58
  10. package/dist/worker.js.map +3 -3
  11. package/package.json +1 -1
  12. package/templates/bootstrap-instructions.md +2 -2
  13. package/templates/modules/architecture-plan/agents/ui-designer/skills/design-system.md +1 -1
  14. package/templates/modules/architecture-plan/skills/architecture-plan.md +1 -1
  15. package/templates/modules/auto-assign/README.md +9 -7
  16. package/templates/modules/auto-assign/agents/ceo/heartbeat-section.md +3 -1
  17. package/templates/modules/auto-assign/agents/ceo/skills/auto-assign.fallback.md +14 -8
  18. package/templates/modules/auto-assign/agents/product-owner/heartbeat-section.md +3 -1
  19. package/templates/modules/auto-assign/module.meta.json +3 -3
  20. package/templates/modules/auto-assign/skills/auto-assign.md +2 -2
  21. package/templates/modules/backlog/agents/ceo/heartbeat-section.md +1 -1
  22. package/templates/modules/backlog/agents/ceo/skills/backlog-health.fallback.md +2 -0
  23. package/templates/modules/backlog/agents/product-owner/heartbeat-section.md +1 -1
  24. package/templates/modules/backlog/docs/backlog-process.md +45 -8
  25. package/templates/modules/backlog/docs/backlog-template.md +3 -2
  26. package/templates/modules/backlog/module.meta.json +3 -3
  27. package/templates/modules/backlog/skills/backlog-health.bar.md +3 -1
  28. package/templates/modules/backlog/skills/backlog-health.md +8 -5
  29. package/templates/modules/competitive-intel/skills/competitive-tracking.md +1 -1
  30. package/templates/modules/github-repo/README.md +3 -3
  31. package/templates/modules/github-repo/agents/engineer/skills/git-workflow.md +72 -9
  32. package/templates/modules/github-repo/docs/git-workflow.md +65 -6
  33. package/templates/modules/github-repo/module.meta.json +1 -1
  34. package/templates/modules/market-analysis/agents/ux-researcher/skills/market-analysis.md +1 -1
  35. package/templates/modules/market-analysis/skills/market-analysis.md +1 -1
  36. package/templates/modules/pr-review/agents/devops/skills/infra-review.md +6 -8
  37. package/templates/modules/pr-review/agents/engineer/skills/pr-workflow.md +31 -12
  38. package/templates/modules/pr-review/agents/product-owner/skills/product-review.md +3 -2
  39. package/templates/modules/pr-review/agents/ui-designer/skills/design-review.md +4 -6
  40. package/templates/modules/pr-review/agents/ux-researcher/skills/ux-review.md +4 -6
  41. package/templates/modules/pr-review/docs/pr-conventions.md +4 -4
  42. package/templates/modules/pr-review/module.meta.json +1 -1
  43. package/templates/modules/security-audit/skills/threat-model.md +1 -1
  44. package/templates/modules/stall-detection/agents/ceo/heartbeat-section.md +1 -1
  45. package/templates/modules/tech-stack/skills/tech-stack.md +1 -1
  46. package/templates/modules/triage/skills/issue-triage.md +1 -1
  47. package/templates/modules/user-testing/agents/qa/skills/user-testing.md +1 -1
  48. package/templates/modules/user-testing/skills/user-testing.md +1 -1
  49. package/templates/modules/vision-workshop/agents/ceo/skills/vision-workshop.md +1 -1
  50. package/templates/presets/repo-maintenance/preset.meta.json +3 -3
  51. package/templates/roles/audio-designer/role.meta.json +5 -2
  52. package/templates/roles/cmo/role.meta.json +2 -1
  53. package/templates/roles/code-reviewer/AGENTS.md +3 -3
  54. package/templates/roles/code-reviewer/role.meta.json +4 -1
  55. package/templates/roles/cto/role.meta.json +2 -1
  56. package/templates/roles/customer-success/role.meta.json +2 -1
  57. package/templates/roles/devops/role.meta.json +2 -1
  58. package/templates/roles/engineer/AGENTS.md +2 -0
  59. package/templates/roles/engineer/HEARTBEAT.md +1 -1
  60. package/templates/roles/engineer/role.meta.json +2 -1
  61. package/templates/roles/game-artist/role.meta.json +5 -2
  62. package/templates/roles/game-designer/role.meta.json +4 -1
  63. package/templates/roles/level-designer/role.meta.json +4 -1
  64. package/templates/roles/product-owner/AGENTS.md +2 -1
  65. package/templates/roles/product-owner/HEARTBEAT.md +1 -1
  66. package/templates/roles/product-owner/role.meta.json +4 -1
  67. package/templates/roles/qa/role.meta.json +2 -1
  68. package/templates/roles/security-engineer/role.meta.json +2 -1
  69. package/templates/roles/technical-writer/role.meta.json +2 -1
  70. package/templates/roles/ui-designer/role.meta.json +2 -1
  71. package/templates/roles/ux-researcher/role.meta.json +4 -1
package/dist/worker.js CHANGED
@@ -9415,6 +9415,8 @@ async function assembleCompany({
9415
9415
  presetLabels = [],
9416
9416
  enableIsolatedWorktrees = false,
9417
9417
  enableEnrichedPersonas = true,
9418
+ gitUserName,
9419
+ gitUserEmail,
9418
9420
  outputDir,
9419
9421
  templatesDir,
9420
9422
  onProgress = () => {
@@ -9544,7 +9546,7 @@ async function assembleCompany({
9544
9546
  }
9545
9547
  return assignee;
9546
9548
  };
9547
- const resolveReviewGate = (reviewGate) => {
9549
+ const resolveReviewGate = (reviewGate, assignTo) => {
9548
9550
  if (!reviewGate || typeof reviewGate !== "object") return null;
9549
9551
  const reviewersRaw = Array.isArray(reviewGate.reviewers) ? reviewGate.reviewers : [];
9550
9552
  const reviewers = [];
@@ -9558,22 +9560,12 @@ async function assembleCompany({
9558
9560
  }
9559
9561
  const approver = typeof reviewGate.approver === "string" && allRoles.has(reviewGate.approver) ? reviewGate.approver : void 0;
9560
9562
  let mergeGate = typeof reviewGate.mergeGate === "string" && allRoles.has(reviewGate.mergeGate) ? reviewGate.mergeGate : void 0;
9561
- if (!mergeGate) {
9562
- const mergeGateFallbacks = [
9563
- "code-reviewer",
9564
- "devops",
9565
- "ui-designer",
9566
- "ux-researcher",
9567
- "security-engineer",
9568
- "qa"
9569
- ];
9570
- mergeGate = mergeGateFallbacks.find((role) => allRoles.has(role) && role !== approver);
9571
- }
9563
+ if (mergeGate === assignTo) mergeGate = void 0;
9572
9564
  if (approver) {
9573
9565
  const idx = reviewers.indexOf(approver);
9574
9566
  if (idx !== -1) reviewers.splice(idx, 1);
9575
9567
  }
9576
- if (reviewers.length === 0 && !approver && !mergeGate) return null;
9568
+ if (!mergeGate) return null;
9577
9569
  return { reviewers, approver, mergeGate };
9578
9570
  };
9579
9571
  const hasCi = moduleNames.includes("ci-cd");
@@ -10080,7 +10072,9 @@ Read: \`docs/${doc}\`
10080
10072
  if (!workspace.cwd) workspace.cwd = localCwd;
10081
10073
  const trimmedSetup = typeof workspace.setupCommand === "string" ? workspace.setupCommand.trim() : "";
10082
10074
  if (!trimmedSetup || trimmedSetup === "git init -b main" || trimmedSetup === "git init") {
10083
- workspace.setupCommand = "git init -b main && git -c user.email=bootstrap@paperclip.local -c user.name='Paperclip Bootstrap' commit --allow-empty -m 'chore: initialize repository'";
10075
+ const gitName = gitUserName || "Paperclip Bootstrap";
10076
+ const gitEmail = gitUserEmail || "bootstrap@paperclip.local";
10077
+ workspace.setupCommand = `git init -b main && git -c user.email=${gitEmail} -c user.name='${gitName.replace(/'/g, "'\\''")}' commit --allow-empty -m 'chore: initialize repository'`;
10084
10078
  }
10085
10079
  }
10086
10080
  return workspace;
@@ -10305,7 +10299,7 @@ Read: \`docs/${doc}\`
10305
10299
  ["goalId", goalRef ? `\u2192 "${goalRef}"` : void 0],
10306
10300
  ["labelIds", `\u2192 [${issueLabelNames.map((name) => `"${name}"`).join(", ")}]`]
10307
10301
  ]);
10308
- const issueReviewGate = resolveReviewGate(issue.reviewGate);
10302
+ const issueReviewGate = resolveReviewGate(issue.reviewGate, issue.assignTo);
10309
10303
  if (issueReviewGate) {
10310
10304
  bootstrap += renderReviewGate(issueReviewGate);
10311
10305
  }
@@ -10330,7 +10324,7 @@ Read: \`docs/${doc}\`
10330
10324
  `;
10331
10325
  if (moduleNames.includes("pr-review")) {
10332
10326
  const ciClause = hasCi ? "CI (lint/test/build) must be green before the merge gate merges \u2014 this is the hard gate and cannot be skipped" : "no CI is configured, so the merge-gate agent must run the test suite/build and paste the real output into the merge-gate verdict before merging \u2014 this is the hard gate";
10333
- bootstrap += `- Required PR reviews use the issue's \`executionPolicy\`. The substantive gate is execution, not opinion: ${ciClause}. Stages, in order: a \`review\` stage for QA when present (test adequacy / running the tests), a \`review\` stage for the Security Engineer **only when the change is security-relevant** (auth, secrets, input boundaries, crypto, dependencies, infra exposure), an \`approval\` stage for the Product Owner (intent/scope), then a final \`approval\` merge-gate stage for the **Code Reviewer** (a non-author who satisfies the hard gate above, merges the PR, then records approval to close the issue). **Never list the issue's executor/author as a participant in any stage** \u2014 Paperclip excludes the original executor from review/approval, so a stage whose only participant is the author has no eligible participant and the issue stalls in \`in_review\` (422 No eligible approval participant); this is why the merge gate is the Code Reviewer (or another present non-author), not the engineer who wrote the code. The merge gate must be last so the Product Owner's approval does not auto-close the issue with the PR still open. Other domain reviewers may add advisory, non-blocking comments but do not gate the merge. Every verdict must cite executed verification. Resolve each role to its agentId. Model review stages in executionPolicy rather than child issues or @-mentions.
10327
+ bootstrap += `- Required PR reviews use the issue's \`executionPolicy\`. The substantive gate is execution, not opinion: ${ciClause}. Stages, in order: a \`review\` stage for QA when present (test adequacy / running the tests), a \`review\` stage for the Security Engineer **only when the change is security-relevant** (auth, secrets, input boundaries, crypto, dependencies, infra exposure), an \`approval\` stage for the Product Owner when present (intent/scope), then a final \`approval\` merge-gate stage for the **Code Reviewer** (a non-author who satisfies the hard gate above, merges the PR, then records approval to close the issue). **Never list the issue's executor/author as a participant in any stage** \u2014 Paperclip excludes the original executor from review/approval, so a stage whose only participant is the author has no eligible participant and the issue stalls in \`in_review\` (422 No eligible approval participant); this is why the merge gate is the Code Reviewer (a non-author), not the engineer who wrote the code. The merge gate must be last so the Product Owner's approval does not auto-close the issue with the PR still open. When no Code Reviewer is on the team, do not set executionPolicy stages at all \u2014 use the PR-Self-Merge path (the engineer opens the PR and merges via \`gh pr merge <N> --merge\`); other review roles may leave advisory comments but do not block. Other domain reviewers may add advisory, non-blocking comments but do not gate the merge. Every verdict must cite executed verification. Resolve each role to its agentId. Model review stages in executionPolicy rather than child issues or @-mentions.
10334
10328
  `;
10335
10329
  }
10336
10330
  bootstrap += `
@@ -10449,6 +10443,8 @@ var PaperclipClient = class {
10449
10443
  this.credentials = credentials;
10450
10444
  this.sessionCookie = null;
10451
10445
  this.boardUserId = null;
10446
+ this.boardUserName = null;
10447
+ this.boardUserEmail = null;
10452
10448
  }
10453
10449
  async _fetch(path3, opts = {}) {
10454
10450
  const url = `${this.baseUrl}${path3}`;
@@ -10490,6 +10486,8 @@ var PaperclipClient = class {
10490
10486
  }
10491
10487
  if (res.ok) {
10492
10488
  this.boardUserId = "local-board";
10489
+ this.boardUserName = null;
10490
+ this.boardUserEmail = null;
10493
10491
  return;
10494
10492
  }
10495
10493
  if (res.status === 401 || res.status === 403) {
@@ -10504,6 +10502,8 @@ var PaperclipClient = class {
10504
10502
  try {
10505
10503
  const session = await this._fetch("/api/auth/get-session");
10506
10504
  this.boardUserId = session?.user?.id || null;
10505
+ this.boardUserName = session?.user?.name || null;
10506
+ this.boardUserEmail = session?.user?.email || null;
10507
10507
  } catch {
10508
10508
  }
10509
10509
  return;
@@ -10603,6 +10603,17 @@ var PaperclipClient = class {
10603
10603
  })
10604
10604
  });
10605
10605
  }
10606
+ async getInstructionsBundle(agentId) {
10607
+ return this._fetch(`/api/agents/${agentId}/instructions-bundle`, {
10608
+ method: "GET"
10609
+ });
10610
+ }
10611
+ async deleteInstructionsBundleFile(agentId, { path: path3 }) {
10612
+ return this._fetch(`/api/agents/${agentId}/instructions-bundle/file`, {
10613
+ method: "DELETE",
10614
+ body: JSON.stringify({ path: path3 })
10615
+ });
10616
+ }
10606
10617
  async createAgent(companyId, agent) {
10607
10618
  const {
10608
10619
  name,
@@ -10772,6 +10783,23 @@ var PaperclipClient = class {
10772
10783
  })
10773
10784
  });
10774
10785
  }
10786
+ async listRoutines(companyId, filters = {}) {
10787
+ const params = new URLSearchParams();
10788
+ if (filters.projectId) params.set("projectId", filters.projectId);
10789
+ const query = params.toString();
10790
+ return this._fetch(`/api/companies/${companyId}/routines${query ? `?${query}` : ""}`, {
10791
+ method: "GET"
10792
+ });
10793
+ }
10794
+ async getRoutine(routineId) {
10795
+ return this._fetch(`/api/routines/${routineId}`, { method: "GET" });
10796
+ }
10797
+ async updateRoutine(routineId, updates = {}) {
10798
+ return this._fetch(`/api/routines/${routineId}`, {
10799
+ method: "PATCH",
10800
+ body: JSON.stringify(updates || {})
10801
+ });
10802
+ }
10775
10803
  async createRoutineTrigger(routineId, { kind, cronExpression, timezone }) {
10776
10804
  return this._fetch(`/api/routines/${routineId}/triggers`, {
10777
10805
  method: "POST",
@@ -10782,6 +10810,12 @@ var PaperclipClient = class {
10782
10810
  })
10783
10811
  });
10784
10812
  }
10813
+ async updateRoutineTrigger(triggerId, updates = {}) {
10814
+ return this._fetch(`/api/routine-triggers/${triggerId}`, {
10815
+ method: "PATCH",
10816
+ body: JSON.stringify(updates || {})
10817
+ });
10818
+ }
10785
10819
  async triggerHeartbeat(agentId, { issueId } = {}) {
10786
10820
  return this._fetch(`/api/agents/${agentId}/wakeup`, {
10787
10821
  method: "POST",
@@ -10969,7 +11003,7 @@ var __dirname = path2.dirname(fileURLToPath2(import.meta.url));
10969
11003
  var DEFAULT_TEMPLATES_REPO_URL = "https://github.com/starlein/paperclip-plugin-company-wizard/tree/main/templates";
10970
11004
  var BUNDLED_TEMPLATES_DIR = path2.resolve(__dirname, "..", "templates");
10971
11005
  var PLUGIN_PACKAGE_NAME = "@starlein/paperclip-plugin-company-wizard";
10972
- var CURRENT_PLUGIN_VERSION = "0.4.5";
11006
+ var CURRENT_PLUGIN_VERSION = "0.4.7";
10973
11007
  var NPM_LATEST_URL = "https://registry.npmjs.org/@starlein%2Fpaperclip-plugin-company-wizard/latest";
10974
11008
  function copyDirSync(src, dest) {
10975
11009
  fs2.mkdirSync(dest, { recursive: true });
@@ -11017,6 +11051,9 @@ function downloadTemplatesFromGithub(destDir, githubUrl) {
11017
11051
  }
11018
11052
  }
11019
11053
  }
11054
+ function isDockerLayout() {
11055
+ return fs2.existsSync(path2.join(os.homedir(), "instances"));
11056
+ }
11020
11057
  async function ensureTemplatesDir(cfg) {
11021
11058
  const repoUrl = cfg.templatesRepoUrl || DEFAULT_TEMPLATES_REPO_URL;
11022
11059
  if (cfg.templatesPath) {
@@ -11024,6 +11061,15 @@ async function ensureTemplatesDir(cfg) {
11024
11061
  downloadTemplatesFromGithub(cfg.templatesPath, repoUrl);
11025
11062
  return cfg.templatesPath;
11026
11063
  }
11064
+ if (isDockerLayout()) {
11065
+ const dockerTemplatesDir = path2.join(os.homedir(), "plugin-templates");
11066
+ if (fs2.existsSync(dockerTemplatesDir)) return dockerTemplatesDir;
11067
+ try {
11068
+ downloadTemplatesFromGithub(dockerTemplatesDir, repoUrl);
11069
+ return dockerTemplatesDir;
11070
+ } catch {
11071
+ }
11072
+ }
11027
11073
  const defaultDir = path2.join(os.homedir(), ".paperclip", "plugin-templates");
11028
11074
  if (fs2.existsSync(defaultDir)) return defaultDir;
11029
11075
  try {
@@ -11136,10 +11182,6 @@ function loadTemplates(templatesDir) {
11136
11182
  loadErrors: [...presetLoad.errors, ...moduleLoad.errors, ...roleLoad.errors]
11137
11183
  };
11138
11184
  }
11139
- function cfgBool(cfg, key) {
11140
- const raw = cfg[key];
11141
- return raw === true || typeof raw === "string" && raw.toLowerCase() === "true";
11142
- }
11143
11185
  async function resolveEnableIsolatedWorkspacesFromInstance(cfg, log) {
11144
11186
  const paperclipUrl = cfg.paperclipUrl || process.env.PAPERCLIP_PUBLIC_URL || "http://localhost:3100";
11145
11187
  const paperclipEmail = cfg.paperclipEmail || "";
@@ -11184,11 +11226,12 @@ function resolveWritableCompaniesDir(cfg, log) {
11184
11226
  );
11185
11227
  }
11186
11228
  }
11187
- const candidates = [
11188
- resolveCompaniesDir(cfg),
11189
- path2.join("/paperclip", "instances", "default", "companies"),
11190
- path2.join(os.tmpdir(), "paperclip-companies")
11191
- ];
11229
+ const candidates = [];
11230
+ if (isDockerLayout()) {
11231
+ candidates.push(path2.join(os.homedir(), "instances", "default", "companies"));
11232
+ }
11233
+ candidates.push(resolveCompaniesDir(cfg));
11234
+ candidates.push(path2.join(os.tmpdir(), "paperclip-companies"));
11192
11235
  const attempted = /* @__PURE__ */ new Set();
11193
11236
  let lastError = "";
11194
11237
  for (const candidate of candidates) {
@@ -11207,6 +11250,51 @@ function resolveWritableCompaniesDir(cfg, log) {
11207
11250
  `Unable to prepare a writable companies directory. Last attempt failed at ${lastError}`
11208
11251
  );
11209
11252
  }
11253
+ function isPathInside(parent, child) {
11254
+ const relative = path2.relative(parent, child);
11255
+ return relative === "" || !!relative && !relative.startsWith("..") && !path2.isAbsolute(relative);
11256
+ }
11257
+ function normalizeGitBranch(value) {
11258
+ const branch = typeof value === "string" && value.trim() ? value.trim() : "main";
11259
+ return /^[A-Za-z0-9._/-]+$/.test(branch) ? branch.replace(/^origin\//, "") : "main";
11260
+ }
11261
+ function prepareLocalProjectWorkspace(mainProject, companyDir, log, gitIdentity) {
11262
+ const workspace = mainProject?.workspace;
11263
+ if (!workspace || workspace.sourceType !== "local_path") return;
11264
+ const cwd = typeof workspace.cwd === "string" ? workspace.cwd.trim() : "";
11265
+ if (!cwd) return;
11266
+ const resolvedCompanyDir = path2.resolve(companyDir);
11267
+ const resolvedCwd = path2.resolve(cwd);
11268
+ if (!isPathInside(resolvedCompanyDir, resolvedCwd)) {
11269
+ log?.(`\u26A0 Skipped project workspace preparation outside company dir: ${resolvedCwd}`);
11270
+ return;
11271
+ }
11272
+ fs2.mkdirSync(resolvedCwd, { recursive: true });
11273
+ const gitDir = path2.join(resolvedCwd, ".git");
11274
+ if (fs2.existsSync(gitDir)) {
11275
+ log?.(`\u2713 Project workspace ready: ${resolvedCwd}`);
11276
+ return;
11277
+ }
11278
+ const branch = normalizeGitBranch(workspace.defaultRef);
11279
+ const gitUserName = gitIdentity?.name || "Paperclip Bootstrap";
11280
+ const gitUserEmail = gitIdentity?.email || "bootstrap@paperclip.local";
11281
+ execFileSync("git", ["init", "-b", branch], { cwd: resolvedCwd, stdio: "pipe" });
11282
+ execFileSync(
11283
+ "git",
11284
+ [
11285
+ "-c",
11286
+ `user.email=${gitUserEmail}`,
11287
+ "-c",
11288
+ `user.name=${gitUserName}`,
11289
+ "commit",
11290
+ "--allow-empty",
11291
+ "-m",
11292
+ "chore: initialize repository"
11293
+ ],
11294
+ { cwd: resolvedCwd, stdio: "pipe" }
11295
+ );
11296
+ log?.(`\u2713 Project workspace initialized: ${resolvedCwd}`);
11297
+ }
11210
11298
  function formatRoleName(role) {
11211
11299
  return role.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
11212
11300
  }
@@ -11264,6 +11352,156 @@ async function syncAgentInstructionsIntoManagedBundle({
11264
11352
  );
11265
11353
  }
11266
11354
  }
11355
+ function preserveExistingSkillSync(existingAgent, nextAdapterConfig) {
11356
+ const existingSync = existingAgent?.adapterConfig && typeof existingAgent.adapterConfig === "object" && "paperclipSkillSync" in existingAgent.adapterConfig ? existingAgent.adapterConfig.paperclipSkillSync : void 0;
11357
+ if (!existingSync) {
11358
+ return nextAdapterConfig;
11359
+ }
11360
+ return {
11361
+ ...nextAdapterConfig,
11362
+ paperclipSkillSync: existingSync
11363
+ };
11364
+ }
11365
+ function routineTemplateTitle(routine) {
11366
+ if (typeof routine?.title === "string" && routine.title.trim()) return routine.title.trim();
11367
+ if (typeof routine?.name === "string" && routine.name.trim()) return routine.name.trim();
11368
+ return "";
11369
+ }
11370
+ function buildWizardManifest(params) {
11371
+ const routineTitles = params.initialRoutines.map((r) => routineTemplateTitle(r)).filter(Boolean);
11372
+ const generatedFilePaths = {};
11373
+ if (params.assembleResult?.allFiles && typeof params.assembleResult.allFiles === "object") {
11374
+ for (const relativePath of Object.keys(params.assembleResult.allFiles)) {
11375
+ const match = relativePath.match(/^agents\/([^/]+)\//);
11376
+ if (match) {
11377
+ const agentRole = match[1];
11378
+ if (!generatedFilePaths[agentRole]) generatedFilePaths[agentRole] = [];
11379
+ generatedFilePaths[agentRole].push(relativePath);
11380
+ } else if (relativePath.startsWith("docs/")) {
11381
+ if (!generatedFilePaths["docs"]) generatedFilePaths["docs"] = [];
11382
+ generatedFilePaths["docs"].push(relativePath);
11383
+ }
11384
+ }
11385
+ }
11386
+ return {
11387
+ pluginVersion: CURRENT_PLUGIN_VERSION,
11388
+ preset: params.presetName,
11389
+ modules: params.selectedModules,
11390
+ roles: params.selectedRoleNames,
11391
+ generatedFilePaths,
11392
+ routineTitles,
11393
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
11394
+ };
11395
+ }
11396
+ var WIZARD_PLUGIN_KEY = "starlein.paperclip-plugin-company-wizard";
11397
+ async function findPluginId(client) {
11398
+ try {
11399
+ const plugins = await client._fetch("/api/plugins");
11400
+ const match = Array.isArray(plugins) ? plugins.find((p) => p.pluginKey === WIZARD_PLUGIN_KEY) : null;
11401
+ return match?.id ?? null;
11402
+ } catch {
11403
+ return null;
11404
+ }
11405
+ }
11406
+ async function syncRoutineTrigger({
11407
+ client,
11408
+ routineId,
11409
+ schedule,
11410
+ log
11411
+ }) {
11412
+ if (!schedule) return;
11413
+ try {
11414
+ const detail = await client.getRoutine(routineId);
11415
+ const triggers = Array.isArray(detail?.triggers) ? detail.triggers : [];
11416
+ const scheduleTrigger = triggers.find((trigger) => trigger?.kind === "schedule");
11417
+ if (scheduleTrigger?.id) {
11418
+ await client.updateRoutineTrigger(scheduleTrigger.id, {
11419
+ enabled: true,
11420
+ cronExpression: schedule,
11421
+ timezone: scheduleTrigger.timezone || "UTC"
11422
+ });
11423
+ return;
11424
+ }
11425
+ await client.createRoutineTrigger(routineId, {
11426
+ kind: "schedule",
11427
+ cronExpression: schedule,
11428
+ timezone: "UTC"
11429
+ });
11430
+ } catch (err) {
11431
+ log(
11432
+ `\u26A0 Could not sync trigger for routine "${routineId}": ${err instanceof Error ? err.message : String(err)}`
11433
+ );
11434
+ }
11435
+ }
11436
+ async function syncExistingCompanyRoutines({
11437
+ client,
11438
+ companyId,
11439
+ routines,
11440
+ ceoAgentId,
11441
+ teamAgentIds,
11442
+ log
11443
+ }) {
11444
+ if (!Array.isArray(routines) || routines.length === 0) return;
11445
+ let existingRoutines = [];
11446
+ try {
11447
+ existingRoutines = await client.listRoutines(companyId);
11448
+ } catch (err) {
11449
+ log(
11450
+ `\u26A0 Could not list existing routines for template sync: ${err instanceof Error ? err.message : String(err)}`
11451
+ );
11452
+ return;
11453
+ }
11454
+ const byTitle = /* @__PURE__ */ new Map();
11455
+ for (const existing of existingRoutines) {
11456
+ if (typeof existing?.title === "string" && existing.title.trim()) {
11457
+ byTitle.set(existing.title.trim().toLowerCase(), existing);
11458
+ }
11459
+ }
11460
+ for (const routine of routines) {
11461
+ const title = routineTemplateTitle(routine);
11462
+ if (!title) continue;
11463
+ const role = routine.assignTo;
11464
+ const assigneeAgentId = !role || role === "ceo" ? ceoAgentId : teamAgentIds[role] ?? ceoAgentId;
11465
+ const payload = {
11466
+ title,
11467
+ description: routine.description || null,
11468
+ assigneeAgentId,
11469
+ priority: routine.priority || "medium",
11470
+ status: routine.status || "active",
11471
+ concurrencyPolicy: routine.concurrencyPolicy || "skip_if_active",
11472
+ catchUpPolicy: routine.catchUpPolicy || "skip_missed"
11473
+ };
11474
+ const existing = byTitle.get(title.toLowerCase());
11475
+ try {
11476
+ if (existing?.id) {
11477
+ await client.updateRoutine(existing.id, payload);
11478
+ await syncRoutineTrigger({
11479
+ client,
11480
+ routineId: existing.id,
11481
+ schedule: routine.schedule,
11482
+ log
11483
+ });
11484
+ log(`\u2713 Synced existing routine "${title}"`);
11485
+ } else {
11486
+ const created = await client.createRoutine(companyId, payload);
11487
+ if (routine.schedule && created?.id) {
11488
+ await client.createRoutineTrigger(created.id, {
11489
+ kind: "schedule",
11490
+ cronExpression: routine.schedule,
11491
+ timezone: "UTC"
11492
+ });
11493
+ }
11494
+ log(
11495
+ `\u2713 Created missing routine "${title}"${routine.schedule ? ` (${routine.schedule})` : ""}`
11496
+ );
11497
+ }
11498
+ } catch (err) {
11499
+ log(
11500
+ `\u26A0 Could not sync routine "${title}": ${err instanceof Error ? err.message : String(err)}`
11501
+ );
11502
+ }
11503
+ }
11504
+ }
11267
11505
  function buildDecisionLogBody({
11268
11506
  companyName,
11269
11507
  companyDescription,
@@ -11465,6 +11703,204 @@ var plugin = definePlugin({
11465
11703
  }
11466
11704
  }
11467
11705
  });
11706
+ ctx.actions.register("preview-company-update", async (params) => {
11707
+ let tmpDir;
11708
+ try {
11709
+ let countFiles2 = function(dir) {
11710
+ if (!fs2.existsSync(dir)) return;
11711
+ for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
11712
+ const full = path2.join(dir, entry.name);
11713
+ if (entry.isDirectory()) countFiles2(full);
11714
+ else if (entry.isFile()) plannedFiles++;
11715
+ }
11716
+ };
11717
+ var countFiles = countFiles2;
11718
+ const existingCompanyId = typeof params.existingCompanyId === "string" && params.existingCompanyId.trim() ? params.existingCompanyId.trim() : "";
11719
+ if (!existingCompanyId) {
11720
+ return { error: "existingCompanyId is required for preview." };
11721
+ }
11722
+ const cfg = await ctx.config.get() ?? {};
11723
+ const paperclipUrl = cfg.paperclipUrl || process.env.PAPERCLIP_PUBLIC_URL || "http://localhost:3100";
11724
+ const paperclipEmail = cfg.paperclipEmail || "";
11725
+ const paperclipPassword = cfg.paperclipPassword || "";
11726
+ const companyName = typeof params.companyName === "string" && params.companyName.trim() ? params.companyName.trim() : "Preview";
11727
+ const templatesDir = await ensureTemplatesDir(cfg);
11728
+ tmpDir = path2.join(os.tmpdir(), `company-wizard-preview-update-${Date.now()}`);
11729
+ const [presets, allModules, roleTemplates] = await Promise.all([
11730
+ loadPresets(templatesDir),
11731
+ loadModules(templatesDir),
11732
+ loadRoles(templatesDir)
11733
+ ]);
11734
+ const roleTemplateByName = new Map(
11735
+ (Array.isArray(roleTemplates) ? roleTemplates : []).filter((role) => role && typeof role.name === "string").map((role) => [role.name, role])
11736
+ );
11737
+ const selectedPreset = presets.find((p) => p.name === params.presetName) || null;
11738
+ const effectiveModules = resolveEffectiveModules(
11739
+ selectedPreset,
11740
+ allModules,
11741
+ params.selectedModules ?? []
11742
+ );
11743
+ const goals = collectGoals(selectedPreset, allModules, new Set(effectiveModules));
11744
+ const presetBootstrapData = collectPresetBootstrapData(selectedPreset);
11745
+ const previewGoals = Array.isArray(params.goals) ? params.goals : params.goal ? [params.goal] : [];
11746
+ const previewProjects = Array.isArray(params.projects) ? params.projects : [];
11747
+ const previewIssues = Array.isArray(params.issues) ? params.issues : [];
11748
+ const assembleResult = await assembleCompany({
11749
+ companyName,
11750
+ userGoals: previewGoals,
11751
+ userProjects: previewProjects,
11752
+ moduleNames: effectiveModules,
11753
+ extraRoleNames: params.selectedRoles ?? [],
11754
+ inlineGoals: goals,
11755
+ userIssues: previewIssues,
11756
+ presetIssues: presetBootstrapData.issues,
11757
+ presetRoutines: presetBootstrapData.routines,
11758
+ presetLabels: presetBootstrapData.labels,
11759
+ enableIsolatedWorktrees: await resolveEnableIsolatedWorkspacesFromInstance(cfg),
11760
+ enableEnrichedPersonas: true,
11761
+ outputDir: tmpDir,
11762
+ templatesDir
11763
+ });
11764
+ const allRoles = Array.isArray(params.allRoles) ? params.allRoles : [...assembleResult.allRoles ?? []].filter(Boolean);
11765
+ const teamRoles = allRoles.filter((r) => r && r !== "ceo");
11766
+ let plannedFiles = 0;
11767
+ countFiles2(assembleResult.companyDir);
11768
+ const client = new PaperclipClient(paperclipUrl, {
11769
+ email: paperclipEmail,
11770
+ password: paperclipPassword
11771
+ });
11772
+ await client.connect();
11773
+ const company = await client.getCompany(existingCompanyId);
11774
+ const existingAgents = await client.listAgents(existingCompanyId);
11775
+ const existingRoutines = await client.listRoutines(existingCompanyId);
11776
+ let existingManifest = null;
11777
+ try {
11778
+ const pluginId = await findPluginId(client);
11779
+ if (pluginId) {
11780
+ const settings = await client._fetch(
11781
+ `/api/plugins/${pluginId}/company-settings/${existingCompanyId}`
11782
+ );
11783
+ const manifestData = settings?.settingsJson?.wizardManifest ?? settings?.settings_json?.wizardManifest;
11784
+ if (manifestData && typeof manifestData === "object") {
11785
+ existingManifest = manifestData;
11786
+ }
11787
+ }
11788
+ } catch {
11789
+ }
11790
+ const existingCeo = Array.isArray(existingAgents) ? existingAgents.find((a) => a?.role === "ceo" && a?.status !== "terminated") : null;
11791
+ const existingByTemplateRole = /* @__PURE__ */ new Map();
11792
+ if (Array.isArray(existingAgents)) {
11793
+ for (const a of existingAgents) {
11794
+ const tr = a?.metadata?.templateRole;
11795
+ if (tr && a?.status !== "terminated" && a?.role !== "ceo") {
11796
+ existingByTemplateRole.set(tr, a);
11797
+ }
11798
+ }
11799
+ }
11800
+ const agents = [];
11801
+ const ceoTemplate = roleTemplateByName.get("ceo") || {};
11802
+ const ceoTitle = typeof ceoTemplate.title === "string" && ceoTemplate.title.trim() ? ceoTemplate.title.trim() : "CEO";
11803
+ agents.push({
11804
+ role: "ceo",
11805
+ title: existingCeo?.title || ceoTitle,
11806
+ action: existingCeo ? "update" : "hire"
11807
+ });
11808
+ for (const roleName of teamRoles) {
11809
+ const roleTemplate = roleTemplateByName.get(roleName) || {};
11810
+ const roleTitle = typeof roleTemplate.title === "string" && roleTemplate.title.trim() ? roleTemplate.title.trim() : formatRoleName(roleName);
11811
+ const existing = existingByTemplateRole.get(roleName);
11812
+ agents.push({
11813
+ role: roleName,
11814
+ title: roleTitle,
11815
+ action: existing ? "update" : "hire"
11816
+ });
11817
+ }
11818
+ const selectedRoleSet = new Set(allRoles);
11819
+ const manifestRoleSet = existingManifest?.roles ? new Set(existingManifest.roles) : null;
11820
+ for (const [tr, agent] of existingByTemplateRole.entries()) {
11821
+ if (!selectedRoleSet.has(tr)) {
11822
+ if (manifestRoleSet && !manifestRoleSet.has(tr)) {
11823
+ continue;
11824
+ }
11825
+ const roleTemplate = roleTemplateByName.get(tr) || {};
11826
+ const roleTitle = typeof roleTemplate.title === "string" && roleTemplate.title.trim() ? roleTemplate.title.trim() : formatRoleName(tr);
11827
+ agents.push({
11828
+ role: tr,
11829
+ title: agent?.title || roleTitle,
11830
+ action: "retire"
11831
+ });
11832
+ }
11833
+ }
11834
+ const existingRoutineByTitle = /* @__PURE__ */ new Map();
11835
+ if (Array.isArray(existingRoutines)) {
11836
+ for (const r of existingRoutines) {
11837
+ const title = routineTemplateTitle(r);
11838
+ if (title) existingRoutineByTitle.set(title.toLowerCase(), r);
11839
+ }
11840
+ }
11841
+ const plannedRoutines = [
11842
+ ...assembleResult.initialRoutines ?? [],
11843
+ ...presetBootstrapData.routines ?? []
11844
+ ];
11845
+ const seenRoutineTitles = /* @__PURE__ */ new Set();
11846
+ const routines = [];
11847
+ for (const routine of plannedRoutines) {
11848
+ const title = routineTemplateTitle(routine);
11849
+ if (!title) continue;
11850
+ const key = title.toLowerCase();
11851
+ if (seenRoutineTitles.has(key)) continue;
11852
+ seenRoutineTitles.add(key);
11853
+ const existing = existingRoutineByTitle.get(key);
11854
+ routines.push({
11855
+ title,
11856
+ action: existing ? "update" : "create",
11857
+ assignTo: routine.assignTo || void 0
11858
+ });
11859
+ }
11860
+ const desiredSkillsPreserved = [];
11861
+ if (Array.isArray(existingAgents)) {
11862
+ for (const a of existingAgents) {
11863
+ if (a?.adapterConfig && typeof a.adapterConfig === "object" && "paperclipSkillSync" in a.adapterConfig) {
11864
+ const sync = a.adapterConfig.paperclipSkillSync;
11865
+ if (sync && typeof sync === "object" && "desiredSkills" in sync) {
11866
+ const skills = sync.desiredSkills;
11867
+ if (Array.isArray(skills) && skills.length > 0) {
11868
+ desiredSkillsPreserved.push({
11869
+ agentId: a.id,
11870
+ agentName: a.title || a.name || a.id,
11871
+ skills
11872
+ });
11873
+ }
11874
+ }
11875
+ }
11876
+ }
11877
+ }
11878
+ try {
11879
+ fs2.rmSync(tmpDir, { recursive: true, force: true });
11880
+ } catch {
11881
+ }
11882
+ tmpDir = void 0;
11883
+ return {
11884
+ diff: {
11885
+ companyId: existingCompanyId,
11886
+ companyName: company?.name || companyName,
11887
+ agents,
11888
+ routines,
11889
+ desiredSkillsPreserved,
11890
+ plannedFiles,
11891
+ existingManifest
11892
+ }
11893
+ };
11894
+ } catch (err) {
11895
+ if (tmpDir) {
11896
+ try {
11897
+ fs2.rmSync(tmpDir, { recursive: true, force: true });
11898
+ } catch {
11899
+ }
11900
+ }
11901
+ return { error: err instanceof Error ? err.message : String(err) };
11902
+ }
11903
+ });
11468
11904
  ctx.actions.register("check-auth", async () => {
11469
11905
  const cfg = await ctx.config.get() ?? {};
11470
11906
  const paperclipUrl = cfg.paperclipUrl || process.env.PAPERCLIP_PUBLIC_URL || "http://localhost:3100";
@@ -11547,10 +11983,6 @@ var plugin = definePlugin({
11547
11983
  const paperclipUrl = cfg.paperclipUrl || process.env.PAPERCLIP_PUBLIC_URL || "http://localhost:3100";
11548
11984
  const paperclipEmail = cfg.paperclipEmail || "";
11549
11985
  const paperclipPassword = cfg.paperclipPassword || "";
11550
- const disableBoardApprovalOnNewCompanies = cfgBool(
11551
- cfg,
11552
- "disableBoardApprovalOnNewCompanies"
11553
- );
11554
11986
  const enableIsolatedWorktrees = await resolveEnableIsolatedWorkspacesFromInstance(cfg, log);
11555
11987
  const enableEnrichedPersonas = true;
11556
11988
  const companyName = typeof params.companyName === "string" ? params.companyName.trim() : "";
@@ -11573,6 +12005,17 @@ var plugin = definePlugin({
11573
12005
  );
11574
12006
  const goals = collectGoals(selectedPreset, allModules, new Set(effectiveModules));
11575
12007
  const presetBootstrapData = collectPresetBootstrapData(selectedPreset);
12008
+ log("Connecting to Paperclip API...");
12009
+ const client = new PaperclipClient(paperclipUrl, {
12010
+ email: paperclipEmail,
12011
+ password: paperclipPassword
12012
+ });
12013
+ await client.connect();
12014
+ log("Connected.");
12015
+ const gitIdentity = {
12016
+ name: client.boardUserName || null,
12017
+ email: client.boardUserEmail || paperclipEmail || null
12018
+ };
11576
12019
  const outputDir = resolveWritableCompaniesDir(cfg, log);
11577
12020
  log("Assembling company workspace...");
11578
12021
  const companyDescription = typeof params.companyDescription === "string" ? params.companyDescription.trim() : "";
@@ -11593,6 +12036,8 @@ var plugin = definePlugin({
11593
12036
  presetLabels: presetBootstrapData.labels,
11594
12037
  enableIsolatedWorktrees,
11595
12038
  enableEnrichedPersonas,
12039
+ gitUserName: gitIdentity.name || void 0,
12040
+ gitUserEmail: gitIdentity.email || void 0,
11596
12041
  outputDir,
11597
12042
  templatesDir,
11598
12043
  onProgress: log
@@ -11610,20 +12055,13 @@ var plugin = definePlugin({
11610
12055
  log(`\u270E Override: ${relPath}`);
11611
12056
  }
11612
12057
  }
12058
+ prepareLocalProjectWorkspace(assembleResult.mainProject, companyDir, log, gitIdentity);
11613
12059
  const ceoInstructionsDir = path2.join(companyDir, "agents", "ceo");
11614
12060
  const ceoEntryFile = "AGENTS.md";
11615
12061
  const ceoEntryPath = path2.join(ceoInstructionsDir, ceoEntryFile);
11616
12062
  const ceoPromptTemplate = fs2.existsSync(ceoEntryPath) ? fs2.readFileSync(ceoEntryPath, "utf-8") : "";
11617
12063
  log("");
11618
12064
  log(`\u2713 Generated files: ${companyDir}`);
11619
- log("Connecting to Paperclip API...");
11620
- const client = new PaperclipClient(paperclipUrl, {
11621
- email: paperclipEmail,
11622
- password: paperclipPassword
11623
- });
11624
- await client.connect();
11625
- log("Connected.");
11626
- log("");
11627
12065
  let company;
11628
12066
  let companyId;
11629
12067
  let createdCompany = false;
@@ -11642,31 +12080,38 @@ var plugin = definePlugin({
11642
12080
  companyId = company.id;
11643
12081
  createdCompany = true;
11644
12082
  log(`\u2713 Company "${companyName}" created`);
11645
- if (disableBoardApprovalOnNewCompanies) {
11646
- try {
11647
- await client.updateCompany(companyId, { requireBoardApprovalForNewAgents: false });
11648
- log("\u2713 Disabled board-approval hiring policy for this new company");
11649
- } catch (err) {
11650
- log(
11651
- `\u26A0 Could not disable board-approval hiring for new agents: ${err instanceof Error ? err.message : String(err)}`
11652
- );
11653
- log(" Continuing \u2014 agent creation will use an approval-aware fallback if required.");
11654
- }
11655
- } else {
11656
- log("Keeping company hire policy as configured (board approvals may be required).");
11657
- }
12083
+ log("Keeping company hire policy as configured (board approvals may be required).");
11658
12084
  }
11659
12085
  let ceoAgentId;
11660
12086
  const teamAgentIds = {};
11661
12087
  let boardOperationsIssue = null;
11662
12088
  let hiringPlanIssue = null;
11663
12089
  let bootstrapIssue;
12090
+ let allRoleNames = [];
12091
+ let existingManifest = null;
12092
+ let existingByTemplateRole = /* @__PURE__ */ new Map();
12093
+ let routines = [];
11664
12094
  try {
11665
- const allRoleNames = [...assembleResult.allRoles ?? []].filter(Boolean).sort();
12095
+ allRoleNames = [...assembleResult.allRoles ?? []].filter(Boolean).sort();
12096
+ if (existingCompanyId) {
12097
+ try {
12098
+ const pluginId = await findPluginId(client);
12099
+ if (pluginId) {
12100
+ const settings = await client._fetch(
12101
+ `/api/plugins/${pluginId}/company-settings/${companyId}`
12102
+ );
12103
+ const manifestData = settings?.settingsJson?.wizardManifest ?? settings?.settings_json?.wizardManifest;
12104
+ if (manifestData && typeof manifestData === "object") {
12105
+ existingManifest = manifestData;
12106
+ }
12107
+ }
12108
+ } catch {
12109
+ }
12110
+ }
11666
12111
  const repositoryMode = userProjects.some(
11667
12112
  (project) => project?.repoUrl || project?.workspace?.sourceType === "git_repo"
11668
12113
  ) ? "existing git repository" : "fresh local repository";
11669
- const approvalMode = disableBoardApprovalOnNewCompanies ? "board approval disabled for this new company by plugin setting" : "board approval preserved; /agent-hires may create pending approvals";
12114
+ const approvalMode = "board approval preserved; /agent-hires may create pending approvals";
11670
12115
  log("Creating board operations and hiring plan records...");
11671
12116
  const createdBoardOperationsIssue = await client.createIssue(companyId, {
11672
12117
  title: "Board Operations",
@@ -11749,7 +12194,7 @@ var plugin = definePlugin({
11749
12194
  try {
11750
12195
  const ceoPatch = {
11751
12196
  adapterType,
11752
- adapterConfig,
12197
+ adapterConfig: preserveExistingSkillSync(existingCeo, adapterConfig),
11753
12198
  runtimeConfig: ceoRuntimeConfig,
11754
12199
  ...ceoMetadata ? { metadata: { ...existingCeo.metadata ?? {}, ...ceoMetadata } } : {}
11755
12200
  };
@@ -11834,7 +12279,7 @@ var plugin = definePlugin({
11834
12279
  const teamRoles = [...assembleResult.allRoles ?? []].filter(
11835
12280
  (r) => r && r !== "ceo"
11836
12281
  );
11837
- let existingByTemplateRole = /* @__PURE__ */ new Map();
12282
+ existingByTemplateRole = /* @__PURE__ */ new Map();
11838
12283
  if (existingCompanyId && teamRoles.length > 0) {
11839
12284
  const agents = await client.listAgents(companyId);
11840
12285
  if (Array.isArray(agents)) {
@@ -11869,7 +12314,7 @@ var plugin = definePlugin({
11869
12314
  try {
11870
12315
  await client.updateAgent(existingAgent.id, {
11871
12316
  adapterType,
11872
- adapterConfig: roleAdapterConfig,
12317
+ adapterConfig: preserveExistingSkillSync(existingAgent, roleAdapterConfig),
11873
12318
  runtimeConfig: roleRuntimeConfig,
11874
12319
  metadata: { ...existingAgent.metadata ?? {}, ...roleMetadata },
11875
12320
  ...!existingAgent.title && roleTitle ? { title: roleTitle } : {},
@@ -11882,6 +12327,14 @@ var plugin = definePlugin({
11882
12327
  );
11883
12328
  }
11884
12329
  teamAgentIds[roleName] = existingAgent.id;
12330
+ await syncAgentInstructionsIntoManagedBundle({
12331
+ client,
12332
+ agentId: existingAgent.id,
12333
+ sourceDir: roleInstructionsDir,
12334
+ entryFile: "AGENTS.md",
12335
+ log
12336
+ });
12337
+ log(`\u2713 Synced ${roleTitle} instructions from latest templates`);
11885
12338
  continue;
11886
12339
  }
11887
12340
  const roleAgent = await client.createAgent(companyId, {
@@ -11905,8 +12358,8 @@ var plugin = definePlugin({
11905
12358
  );
11906
12359
  }
11907
12360
  }
12361
+ routines = Array.isArray(assembleResult.initialRoutines) ? assembleResult.initialRoutines : [];
11908
12362
  if (!existingCompanyId) {
11909
- const routines = Array.isArray(assembleResult.initialRoutines) ? assembleResult.initialRoutines : [];
11910
12363
  let mainProjectId;
11911
12364
  const mainProject = assembleResult.mainProject;
11912
12365
  if (routines.length > 0 && mainProject?.name) {
@@ -11939,7 +12392,8 @@ var plugin = definePlugin({
11939
12392
  assigneeAgentId,
11940
12393
  projectId: mainProjectId,
11941
12394
  priority: routine.priority || "medium",
11942
- concurrencyPolicy: routine.concurrencyPolicy || "skip_if_active"
12395
+ concurrencyPolicy: routine.concurrencyPolicy || "skip_if_active",
12396
+ catchUpPolicy: routine.catchUpPolicy || "skip_missed"
11943
12397
  });
11944
12398
  if (routine.schedule && createdRoutine?.id) {
11945
12399
  await client.createRoutineTrigger(createdRoutine.id, {
@@ -11957,6 +12411,15 @@ var plugin = definePlugin({
11957
12411
  );
11958
12412
  }
11959
12413
  }
12414
+ } else {
12415
+ await syncExistingCompanyRoutines({
12416
+ client,
12417
+ companyId,
12418
+ routines,
12419
+ ceoAgentId,
12420
+ teamAgentIds,
12421
+ log
12422
+ });
11960
12423
  }
11961
12424
  const bootstrapDescription = fs2.readFileSync(
11962
12425
  path2.join(companyDir, "BOOTSTRAP.md"),
@@ -11997,6 +12460,73 @@ var plugin = definePlugin({
11997
12460
  const issueIds = [boardOperationsIssue?.id, hiringPlanIssue?.id, bootstrapIssue.id].filter(
11998
12461
  Boolean
11999
12462
  );
12463
+ try {
12464
+ const pluginId = await findPluginId(client);
12465
+ if (pluginId) {
12466
+ const wizardManifest = buildWizardManifest({
12467
+ presetName: selectedPreset?.name ?? null,
12468
+ selectedModules: effectiveModules,
12469
+ selectedRoleNames: allRoleNames,
12470
+ assembleResult,
12471
+ initialRoutines: routines
12472
+ });
12473
+ await client._fetch(`/api/plugins/${pluginId}/company-settings/${companyId}`, {
12474
+ method: "PUT",
12475
+ body: JSON.stringify({ settingsJson: { wizardManifest } })
12476
+ });
12477
+ log("\u2713 Wizard manifest saved");
12478
+ } else {
12479
+ log("\u26A0 Could not find plugin ID \u2014 manifest not saved");
12480
+ }
12481
+ } catch (manifestErr) {
12482
+ log(
12483
+ `\u26A0 Could not save wizard manifest: ${manifestErr instanceof Error ? manifestErr.message : String(manifestErr)}`
12484
+ );
12485
+ }
12486
+ if (existingCompanyId && existingManifest && Array.isArray(existingManifest.roles)) {
12487
+ const newRoleSet = new Set(allRoleNames);
12488
+ const retiredRoles = existingManifest.roles.filter(
12489
+ (role) => !newRoleSet.has(role)
12490
+ );
12491
+ for (const role of retiredRoles) {
12492
+ try {
12493
+ const agentData = existingByTemplateRole.get(role);
12494
+ const agentInfo = agentData ? `**Agent ID:** ${agentData.id}
12495
+ **Agent name:** ${agentData.title || agentData.name || agentData.id}` : "_No active agent found for this role._";
12496
+ const issueTitle = `Review retired template role: ${role}`;
12497
+ const issueDescription = [
12498
+ `## Retired Role: \`${role}\``,
12499
+ "",
12500
+ "This template role was removed from the company configuration during an update.",
12501
+ "The agent is still active but no longer managed by the wizard.",
12502
+ "",
12503
+ "### Cleanup Checklist",
12504
+ "",
12505
+ "- [ ] Review the agent's current work and open issues",
12506
+ "- [ ] Reassign any in-progress issues to other team members",
12507
+ "- [ ] Consider pausing the agent's heartbeat",
12508
+ "- [ ] Remove module-specific skill files if applicable",
12509
+ "- [ ] Consider terminating the agent if no longer needed",
12510
+ "",
12511
+ agentInfo
12512
+ ].join("\n");
12513
+ const createdIssue = await client.createIssue(companyId, {
12514
+ title: issueTitle,
12515
+ description: issueDescription,
12516
+ priority: "low",
12517
+ status: "todo",
12518
+ ...boardOperationsIssue?.id ? { projectId: boardOperationsIssue.id, goalId: boardOperationsIssue.id } : {}
12519
+ });
12520
+ log(
12521
+ `\u2713 Retired-role review issue created for "${role}": ${createdIssue.identifier || createdIssue.id}`
12522
+ );
12523
+ } catch (retiredRoleErr) {
12524
+ log(
12525
+ `\u26A0 Could not create retired-role review issue for "${role}": ${retiredRoleErr instanceof Error ? retiredRoleErr.message : String(retiredRoleErr)}`
12526
+ );
12527
+ }
12528
+ }
12529
+ }
12000
12530
  return {
12001
12531
  companyId,
12002
12532
  issuePrefix: company.issuePrefix,
@@ -12039,6 +12569,7 @@ var worker_default = plugin;
12039
12569
  runWorker(plugin, import.meta.url);
12040
12570
  export {
12041
12571
  worker_default as default,
12572
+ prepareLocalProjectWorkspace,
12042
12573
  resolveWritableCompaniesDir
12043
12574
  };
12044
12575
  //# sourceMappingURL=worker.js.map