@starlein/paperclip-plugin-company-wizard 0.4.6 → 0.4.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.
Files changed (99) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +6 -4
  3. package/dist/manifest.js +1 -1
  4. package/dist/manifest.js.map +1 -1
  5. package/dist/ui/index.css +82 -0
  6. package/dist/ui/index.css.map +2 -2
  7. package/dist/ui/index.js +422 -137
  8. package/dist/ui/index.js.map +4 -4
  9. package/dist/worker.js +352 -21
  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/ceo/skills/architecture-plan.bar.md +17 -0
  14. package/templates/modules/architecture-plan/agents/ui-designer/skills/architecture-plan.md +2 -0
  15. package/templates/modules/architecture-plan/agents/ui-designer/skills/design-system.md +5 -4
  16. package/templates/modules/architecture-plan/skills/architecture-plan.md +1 -1
  17. package/templates/modules/architecture-plan/skills/design-system.md +25 -0
  18. package/templates/modules/auto-assign/agents/ceo/heartbeat-section.md +1 -1
  19. package/templates/modules/auto-assign/agents/product-owner/heartbeat-section.md +1 -1
  20. package/templates/modules/backlog/agents/ceo/heartbeat-section.md +1 -1
  21. package/templates/modules/backlog/agents/ceo/skills/backlog-health.fallback.md +2 -0
  22. package/templates/modules/backlog/agents/product-owner/heartbeat-section.md +1 -1
  23. package/templates/modules/backlog/docs/backlog-process.md +38 -1
  24. package/templates/modules/backlog/docs/backlog-template.md +1 -1
  25. package/templates/modules/backlog/module.meta.json +1 -1
  26. package/templates/modules/backlog/skills/backlog-health.bar.md +2 -0
  27. package/templates/modules/backlog/skills/backlog-health.md +7 -4
  28. package/templates/modules/brand-identity/agents/ceo/skills/brand-identity.fallback.md +4 -4
  29. package/templates/modules/brand-identity/skills/brand-identity.md +1 -1
  30. package/templates/modules/ci-cd/agents/engineer/skills/ci-cd.fallback.md +2 -1
  31. package/templates/modules/ci-cd/skills/ci-cd.md +13 -2
  32. package/templates/modules/codebase-onboarding/agents/ceo/skills/codebase-audit.fallback.md +10 -0
  33. package/templates/modules/competitive-intel/agents/product-owner/skills/competitive-tracking.fallback.md +19 -0
  34. package/templates/modules/competitive-intel/skills/competitive-tracking.bar.md +11 -0
  35. package/templates/modules/competitive-intel/skills/competitive-tracking.md +1 -1
  36. package/templates/modules/dependency-management/module.meta.json +9 -0
  37. package/templates/modules/dependency-management/skills/dependency-audit.md +8 -5
  38. package/templates/modules/documentation/skills/project-docs.bar.md +13 -0
  39. package/templates/modules/game-design/skills/game-design.bar.md +13 -0
  40. package/templates/modules/github-repo/agents/engineer/skills/git-workflow.md +79 -17
  41. package/templates/modules/github-repo/docs/git-workflow.md +34 -16
  42. package/templates/modules/market-analysis/agents/ux-researcher/skills/market-analysis.md +1 -1
  43. package/templates/modules/market-analysis/docs/market-analysis-template.md +17 -0
  44. package/templates/modules/market-analysis/skills/market-analysis.md +1 -1
  45. package/templates/modules/monitoring/agents/engineer/skills/monitoring.fallback.md +1 -1
  46. package/templates/modules/monitoring/skills/monitoring.md +3 -1
  47. package/templates/modules/pr-review/agents/code-reviewer/skills/code-review.md +19 -5
  48. package/templates/modules/pr-review/agents/devops/skills/infra-review.md +6 -8
  49. package/templates/modules/pr-review/agents/engineer/skills/pr-workflow.md +47 -12
  50. package/templates/modules/pr-review/agents/product-owner/skills/product-review.md +3 -2
  51. package/templates/modules/pr-review/agents/ui-designer/skills/design-review.md +4 -6
  52. package/templates/modules/pr-review/agents/ux-researcher/skills/ux-review.md +4 -6
  53. package/templates/modules/pr-review/docs/pr-conventions.md +4 -4
  54. package/templates/modules/pr-review/module.meta.json +1 -1
  55. package/templates/modules/release-management/agents/ceo/skills/release-process.fallback.md +20 -0
  56. package/templates/modules/release-management/module.meta.json +9 -0
  57. package/templates/modules/release-management/skills/release-process.md +7 -5
  58. package/templates/modules/security-audit/skills/threat-model.md +1 -1
  59. package/templates/modules/stall-detection/agents/ceo/heartbeat-section.md +1 -1
  60. package/templates/modules/tech-stack/skills/tech-stack.md +1 -1
  61. package/templates/modules/triage/agents/ceo/skills/issue-triage.fallback.md +2 -2
  62. package/templates/modules/triage/skills/issue-triage.md +1 -1
  63. package/templates/modules/user-testing/agents/qa/skills/user-testing.md +1 -1
  64. package/templates/modules/user-testing/skills/user-testing.md +1 -1
  65. package/templates/modules/vision-workshop/agents/ceo/skills/vision-workshop.md +1 -1
  66. package/templates/modules/website-relaunch/module.meta.json +0 -15
  67. package/templates/presets/repo-maintenance/preset.meta.json +3 -3
  68. package/templates/roles/audio-designer/role.meta.json +5 -2
  69. package/templates/roles/ceo/HEARTBEAT.md +1 -1
  70. package/templates/roles/cmo/AGENTS.md +15 -0
  71. package/templates/roles/cmo/HEARTBEAT.md +1 -1
  72. package/templates/roles/cmo/role.meta.json +2 -1
  73. package/templates/roles/code-reviewer/AGENTS.md +3 -3
  74. package/templates/roles/code-reviewer/HEARTBEAT.md +2 -1
  75. package/templates/roles/code-reviewer/role.meta.json +4 -1
  76. package/templates/roles/cto/AGENTS.md +22 -0
  77. package/templates/roles/cto/HEARTBEAT.md +1 -1
  78. package/templates/roles/cto/role.meta.json +2 -1
  79. package/templates/roles/customer-success/role.meta.json +2 -1
  80. package/templates/roles/devops/AGENTS.md +21 -0
  81. package/templates/roles/devops/HEARTBEAT.md +1 -1
  82. package/templates/roles/devops/role.meta.json +2 -1
  83. package/templates/roles/engineer/role.meta.json +2 -1
  84. package/templates/roles/game-artist/role.meta.json +5 -2
  85. package/templates/roles/game-designer/role.meta.json +4 -1
  86. package/templates/roles/level-designer/role.meta.json +4 -1
  87. package/templates/roles/product-owner/SOUL.md +4 -1
  88. package/templates/roles/product-owner/role.meta.json +4 -1
  89. package/templates/roles/qa/HEARTBEAT.md +3 -2
  90. package/templates/roles/qa/role.meta.json +2 -1
  91. package/templates/roles/security-engineer/AGENTS.md +7 -0
  92. package/templates/roles/security-engineer/role.meta.json +2 -1
  93. package/templates/roles/technical-writer/AGENTS.md +1 -1
  94. package/templates/roles/technical-writer/role.meta.json +2 -1
  95. package/templates/roles/ui-designer/HEARTBEAT.md +1 -1
  96. package/templates/roles/ui-designer/role.meta.json +2 -1
  97. package/templates/roles/ux-researcher/AGENTS.md +21 -0
  98. package/templates/roles/ux-researcher/HEARTBEAT.md +1 -1
  99. package/templates/roles/ux-researcher/role.meta.json +4 -1
package/dist/worker.js CHANGED
@@ -9546,7 +9546,7 @@ async function assembleCompany({
9546
9546
  }
9547
9547
  return assignee;
9548
9548
  };
9549
- const resolveReviewGate = (reviewGate) => {
9549
+ const resolveReviewGate = (reviewGate, assignTo) => {
9550
9550
  if (!reviewGate || typeof reviewGate !== "object") return null;
9551
9551
  const reviewersRaw = Array.isArray(reviewGate.reviewers) ? reviewGate.reviewers : [];
9552
9552
  const reviewers = [];
@@ -9560,22 +9560,12 @@ async function assembleCompany({
9560
9560
  }
9561
9561
  const approver = typeof reviewGate.approver === "string" && allRoles.has(reviewGate.approver) ? reviewGate.approver : void 0;
9562
9562
  let mergeGate = typeof reviewGate.mergeGate === "string" && allRoles.has(reviewGate.mergeGate) ? reviewGate.mergeGate : void 0;
9563
- if (!mergeGate) {
9564
- const mergeGateFallbacks = [
9565
- "code-reviewer",
9566
- "devops",
9567
- "ui-designer",
9568
- "ux-researcher",
9569
- "security-engineer",
9570
- "qa"
9571
- ];
9572
- mergeGate = mergeGateFallbacks.find((role) => allRoles.has(role) && role !== approver);
9573
- }
9563
+ if (mergeGate === assignTo) mergeGate = void 0;
9574
9564
  if (approver) {
9575
9565
  const idx = reviewers.indexOf(approver);
9576
9566
  if (idx !== -1) reviewers.splice(idx, 1);
9577
9567
  }
9578
- if (reviewers.length === 0 && !approver && !mergeGate) return null;
9568
+ if (!mergeGate) return null;
9579
9569
  return { reviewers, approver, mergeGate };
9580
9570
  };
9581
9571
  const hasCi = moduleNames.includes("ci-cd");
@@ -10309,7 +10299,7 @@ Read: \`docs/${doc}\`
10309
10299
  ["goalId", goalRef ? `\u2192 "${goalRef}"` : void 0],
10310
10300
  ["labelIds", `\u2192 [${issueLabelNames.map((name) => `"${name}"`).join(", ")}]`]
10311
10301
  ]);
10312
- const issueReviewGate = resolveReviewGate(issue.reviewGate);
10302
+ const issueReviewGate = resolveReviewGate(issue.reviewGate, issue.assignTo);
10313
10303
  if (issueReviewGate) {
10314
10304
  bootstrap += renderReviewGate(issueReviewGate);
10315
10305
  }
@@ -10334,7 +10324,7 @@ Read: \`docs/${doc}\`
10334
10324
  `;
10335
10325
  if (moduleNames.includes("pr-review")) {
10336
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";
10337
- 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.
10338
10328
  `;
10339
10329
  }
10340
10330
  bootstrap += `
@@ -10613,6 +10603,17 @@ var PaperclipClient = class {
10613
10603
  })
10614
10604
  });
10615
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
+ }
10616
10617
  async createAgent(companyId, agent) {
10617
10618
  const {
10618
10619
  name,
@@ -11002,7 +11003,7 @@ var __dirname = path2.dirname(fileURLToPath2(import.meta.url));
11002
11003
  var DEFAULT_TEMPLATES_REPO_URL = "https://github.com/starlein/paperclip-plugin-company-wizard/tree/main/templates";
11003
11004
  var BUNDLED_TEMPLATES_DIR = path2.resolve(__dirname, "..", "templates");
11004
11005
  var PLUGIN_PACKAGE_NAME = "@starlein/paperclip-plugin-company-wizard";
11005
- var CURRENT_PLUGIN_VERSION = "0.4.6";
11006
+ var CURRENT_PLUGIN_VERSION = "0.4.9";
11006
11007
  var NPM_LATEST_URL = "https://registry.npmjs.org/@starlein%2Fpaperclip-plugin-company-wizard/latest";
11007
11008
  function copyDirSync(src, dest) {
11008
11009
  fs2.mkdirSync(dest, { recursive: true });
@@ -11351,11 +11352,57 @@ async function syncAgentInstructionsIntoManagedBundle({
11351
11352
  );
11352
11353
  }
11353
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
+ }
11354
11365
  function routineTemplateTitle(routine) {
11355
11366
  if (typeof routine?.title === "string" && routine.title.trim()) return routine.title.trim();
11356
11367
  if (typeof routine?.name === "string" && routine.name.trim()) return routine.name.trim();
11357
11368
  return "";
11358
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
+ }
11359
11406
  async function syncRoutineTrigger({
11360
11407
  client,
11361
11408
  routineId,
@@ -11656,6 +11703,204 @@ var plugin = definePlugin({
11656
11703
  }
11657
11704
  }
11658
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
+ });
11659
11904
  ctx.actions.register("check-auth", async () => {
11660
11905
  const cfg = await ctx.config.get() ?? {};
11661
11906
  const paperclipUrl = cfg.paperclipUrl || process.env.PAPERCLIP_PUBLIC_URL || "http://localhost:3100";
@@ -11842,8 +12087,27 @@ var plugin = definePlugin({
11842
12087
  let boardOperationsIssue = null;
11843
12088
  let hiringPlanIssue = null;
11844
12089
  let bootstrapIssue;
12090
+ let allRoleNames = [];
12091
+ let existingManifest = null;
12092
+ let existingByTemplateRole = /* @__PURE__ */ new Map();
12093
+ let routines = [];
11845
12094
  try {
11846
- 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
+ }
11847
12111
  const repositoryMode = userProjects.some(
11848
12112
  (project) => project?.repoUrl || project?.workspace?.sourceType === "git_repo"
11849
12113
  ) ? "existing git repository" : "fresh local repository";
@@ -11930,7 +12194,7 @@ var plugin = definePlugin({
11930
12194
  try {
11931
12195
  const ceoPatch = {
11932
12196
  adapterType,
11933
- adapterConfig,
12197
+ adapterConfig: preserveExistingSkillSync(existingCeo, adapterConfig),
11934
12198
  runtimeConfig: ceoRuntimeConfig,
11935
12199
  ...ceoMetadata ? { metadata: { ...existingCeo.metadata ?? {}, ...ceoMetadata } } : {}
11936
12200
  };
@@ -12015,7 +12279,7 @@ var plugin = definePlugin({
12015
12279
  const teamRoles = [...assembleResult.allRoles ?? []].filter(
12016
12280
  (r) => r && r !== "ceo"
12017
12281
  );
12018
- let existingByTemplateRole = /* @__PURE__ */ new Map();
12282
+ existingByTemplateRole = /* @__PURE__ */ new Map();
12019
12283
  if (existingCompanyId && teamRoles.length > 0) {
12020
12284
  const agents = await client.listAgents(companyId);
12021
12285
  if (Array.isArray(agents)) {
@@ -12050,7 +12314,7 @@ var plugin = definePlugin({
12050
12314
  try {
12051
12315
  await client.updateAgent(existingAgent.id, {
12052
12316
  adapterType,
12053
- adapterConfig: roleAdapterConfig,
12317
+ adapterConfig: preserveExistingSkillSync(existingAgent, roleAdapterConfig),
12054
12318
  runtimeConfig: roleRuntimeConfig,
12055
12319
  metadata: { ...existingAgent.metadata ?? {}, ...roleMetadata },
12056
12320
  ...!existingAgent.title && roleTitle ? { title: roleTitle } : {},
@@ -12094,7 +12358,7 @@ var plugin = definePlugin({
12094
12358
  );
12095
12359
  }
12096
12360
  }
12097
- const routines = Array.isArray(assembleResult.initialRoutines) ? assembleResult.initialRoutines : [];
12361
+ routines = Array.isArray(assembleResult.initialRoutines) ? assembleResult.initialRoutines : [];
12098
12362
  if (!existingCompanyId) {
12099
12363
  let mainProjectId;
12100
12364
  const mainProject = assembleResult.mainProject;
@@ -12196,6 +12460,73 @@ var plugin = definePlugin({
12196
12460
  const issueIds = [boardOperationsIssue?.id, hiringPlanIssue?.id, bootstrapIssue.id].filter(
12197
12461
  Boolean
12198
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
+ }
12199
12530
  return {
12200
12531
  companyId,
12201
12532
  issuePrefix: company.issuePrefix,