@starlein/paperclip-plugin-company-wizard 0.3.23 → 0.4.1

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 (68) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +18 -13
  3. package/dist/manifest.js +1 -6
  4. package/dist/manifest.js.map +2 -2
  5. package/dist/ui/index.css +3 -0
  6. package/dist/ui/index.css.map +2 -2
  7. package/dist/ui/index.js +59 -30
  8. package/dist/ui/index.js.map +3 -3
  9. package/dist/worker.js +315 -86
  10. package/dist/worker.js.map +3 -3
  11. package/package.json +1 -1
  12. package/templates/ai-wizard/config-format.md +4 -4
  13. package/templates/ai-wizard/interview-system.md +3 -3
  14. package/templates/ai-wizard/single-shot-system.md +3 -3
  15. package/templates/bootstrap-instructions.md +3 -3
  16. package/templates/modules/architecture-plan/agents/ui-designer/skills/design-system.md +1 -1
  17. package/templates/modules/auto-assign/agents/ceo/heartbeat-section.md +2 -8
  18. package/templates/modules/auto-assign/agents/product-owner/heartbeat-section.md +2 -9
  19. package/templates/modules/auto-assign/module.meta.json +1 -1
  20. package/templates/modules/auto-assign/skills/auto-assign.md +18 -15
  21. package/templates/modules/backlog/agents/ceo/heartbeat-section.md +2 -9
  22. package/templates/modules/backlog/agents/product-owner/heartbeat-section.md +2 -14
  23. package/templates/modules/backlog/module.meta.json +1 -1
  24. package/templates/modules/backlog/skills/backlog-health.md +20 -21
  25. package/templates/modules/codebase-onboarding/skills/codebase-audit.md +29 -30
  26. package/templates/modules/github-repo/agents/engineer/skills/git-workflow.md +11 -7
  27. package/templates/modules/github-repo/docs/git-workflow.md +10 -6
  28. package/templates/modules/hiring-review/skills/hiring-review.md +40 -16
  29. package/templates/modules/market-analysis/agents/ux-researcher/skills/market-analysis.md +1 -1
  30. package/templates/modules/pr-review/README.md +13 -13
  31. package/templates/modules/pr-review/agents/code-reviewer/skills/code-review.md +16 -21
  32. package/templates/modules/pr-review/agents/devops/skills/infra-review.md +1 -1
  33. package/templates/modules/pr-review/agents/engineer/skills/pr-workflow.md +17 -11
  34. package/templates/modules/pr-review/agents/product-owner/skills/product-review.md +1 -1
  35. package/templates/modules/pr-review/agents/qa/skills/qa-review.md +40 -19
  36. package/templates/modules/pr-review/agents/security-engineer/skills/pr-security-review.md +27 -0
  37. package/templates/modules/pr-review/agents/ui-designer/skills/design-review.md +1 -1
  38. package/templates/modules/pr-review/agents/ux-researcher/skills/ux-review.md +1 -1
  39. package/templates/modules/pr-review/docs/pr-conventions.md +35 -23
  40. package/templates/modules/pr-review/module.meta.json +4 -3
  41. package/templates/modules/stall-detection/README.md +8 -8
  42. package/templates/modules/stall-detection/agents/ceo/heartbeat-section.md +2 -11
  43. package/templates/modules/stall-detection/agents/ceo/skills/stall-detection.md +22 -13
  44. package/templates/modules/stall-detection/module.meta.json +1 -1
  45. package/templates/modules/triage/skills/issue-triage.md +20 -33
  46. package/templates/roles/audio-designer/HEARTBEAT.md +37 -21
  47. package/templates/roles/ceo/HEARTBEAT.md +34 -56
  48. package/templates/roles/cmo/HEARTBEAT.md +37 -21
  49. package/templates/roles/code-reviewer/AGENTS.md +1 -1
  50. package/templates/roles/code-reviewer/HEARTBEAT.md +39 -19
  51. package/templates/roles/cto/HEARTBEAT.md +33 -25
  52. package/templates/roles/customer-success/HEARTBEAT.md +39 -19
  53. package/templates/roles/devops/HEARTBEAT.md +36 -25
  54. package/templates/roles/engineer/AGENTS.md +27 -9
  55. package/templates/roles/engineer/HEARTBEAT.md +35 -21
  56. package/templates/roles/game-artist/HEARTBEAT.md +37 -21
  57. package/templates/roles/game-designer/HEARTBEAT.md +37 -21
  58. package/templates/roles/level-designer/HEARTBEAT.md +37 -21
  59. package/templates/roles/product-owner/AGENTS.md +24 -8
  60. package/templates/roles/product-owner/HEARTBEAT.md +37 -19
  61. package/templates/roles/qa/AGENTS.md +26 -11
  62. package/templates/roles/qa/HEARTBEAT.md +37 -21
  63. package/templates/roles/security-engineer/AGENTS.md +21 -23
  64. package/templates/roles/security-engineer/HEARTBEAT.md +39 -19
  65. package/templates/roles/technical-writer/HEARTBEAT.md +39 -18
  66. package/templates/roles/ui-designer/AGENTS.md +26 -9
  67. package/templates/roles/ui-designer/HEARTBEAT.md +37 -21
  68. package/templates/roles/ux-researcher/HEARTBEAT.md +37 -21
package/dist/worker.js CHANGED
@@ -9381,11 +9381,7 @@ function normalizeExecutionBaseRef(ref, fallbackRef) {
9381
9381
  (value) => typeof value === "string" && value.trim()
9382
9382
  );
9383
9383
  const selected = candidates.length > 0 ? String(candidates[0]).trim() : "";
9384
- if (!selected) return "origin/main";
9385
- if (selected.startsWith("origin/") || selected.startsWith("refs/")) {
9386
- return selected;
9387
- }
9388
- return `origin/${selected}`;
9384
+ return selected || null;
9389
9385
  }
9390
9386
  async function assembleCompany({
9391
9387
  companyName,
@@ -9400,7 +9396,7 @@ async function assembleCompany({
9400
9396
  presetRoutines = [],
9401
9397
  presetLabels = [],
9402
9398
  enableIsolatedWorktrees = false,
9403
- enableEnrichedPersonas = false,
9399
+ enableEnrichedPersonas = true,
9404
9400
  outputDir,
9405
9401
  templatesDir,
9406
9402
  onProgress = () => {
@@ -9551,6 +9547,7 @@ async function assembleCompany({
9551
9547
  if (reviewers.length === 0 && !approver && !mergeGate) return null;
9552
9548
  return { reviewers, approver, mergeGate };
9553
9549
  };
9550
+ const hasCi = moduleNames.includes("ci-cd");
9554
9551
  const renderReviewGate = (gate) => {
9555
9552
  const stages = [];
9556
9553
  for (const role of gate.reviewers) {
@@ -9562,12 +9559,14 @@ async function assembleCompany({
9562
9559
  );
9563
9560
  }
9564
9561
  if (gate.mergeGate) {
9562
+ const gatePrecondition = hasCi ? "CI must be green before merge" : "no CI configured \u2014 run the test suite/build and paste the output before merge";
9565
9563
  stages.push(
9566
- ` - stage ${stages.length + 1} (approval) \u2192 assign ${JSON.stringify(gate.mergeGate)} \u2014 merge gate: merge the PR, then record approved to close`
9564
+ ` - stage ${stages.length + 1} (approval) \u2192 assign ${JSON.stringify(gate.mergeGate)} \u2014 merge gate: ${gatePrecondition}; merge the PR, then record approved to close`
9567
9565
  );
9568
9566
  }
9569
9567
  return `- **executionPolicy** (set when creating this issue; resolve each role to its agentId):
9570
9568
  ${stages.join("\n")}
9569
+ - every verdict must cite executed verification (commands + results); "looks good" without evidence is not a valid verdict
9571
9570
 
9572
9571
  `;
9573
9572
  };
@@ -10010,6 +10009,9 @@ Read: \`docs/${doc}\`
10010
10009
  if (allGoals.length > 0) {
10011
10010
  bootstrap += `## Goals
10012
10011
 
10012
+ `;
10013
+ bootstrap += `> **Goal focus:** keep the top-level goal outcome-first and product-first. Secondary constraints (compliance, security, accessibility, performance, tech stack) are quality bars unless the user explicitly made one of them the primary project. Seed issues should lead with core product capabilities and include constraints as acceptance criteria/risk notes, not let one side constraint dominate the backlog.
10014
+
10013
10015
  `;
10014
10016
  for (const g of allGoals) {
10015
10017
  bootstrap += `### ${g.title}
@@ -10076,21 +10078,34 @@ Read: \`docs/${doc}\`
10076
10078
  const isFreshLocalRepo = (workspace) => workspace?.sourceType !== "git_repo";
10077
10079
  const effectiveExecutionPolicy = (proj, workspace) => {
10078
10080
  const policy = proj?.executionWorkspacePolicy;
10079
- if (!policy || typeof policy !== "object") return null;
10081
+ const canUseIsolatedWorktrees = enableIsolatedWorktrees && !isFreshLocalRepo(workspace);
10082
+ if (!policy || typeof policy !== "object") {
10083
+ if (!canUseIsolatedWorktrees) return null;
10084
+ const baseRef = normalizeExecutionBaseRef(null, workspace?.defaultRef || workspace?.repoRef);
10085
+ return {
10086
+ enabled: true,
10087
+ defaultMode: "isolated_workspace",
10088
+ workspaceStrategy: {
10089
+ type: "git_worktree",
10090
+ ...baseRef ? { baseRef } : {}
10091
+ }
10092
+ };
10093
+ }
10080
10094
  if (policy.defaultMode === "isolated_workspace") {
10081
- if (!enableIsolatedWorktrees || isFreshLocalRepo(workspace)) return null;
10095
+ if (!canUseIsolatedWorktrees) return null;
10082
10096
  const strategy = policy.workspaceStrategy;
10083
- if (!strategy || typeof strategy !== "object") return policy;
10084
- if (strategy.type !== "git_worktree") return policy;
10097
+ if (!strategy || typeof strategy !== "object")
10098
+ return { ...policy, enabled: policy.enabled ?? true };
10099
+ if (strategy.type !== "git_worktree") return { ...policy, enabled: policy.enabled ?? true };
10085
10100
  const workspaceStrategy = { ...strategy };
10086
- const normalizedBaseRef = normalizeExecutionBaseRef(
10101
+ const resolvedBaseRef = normalizeExecutionBaseRef(
10087
10102
  workspaceStrategy.baseRef,
10088
- workspace?.repoRef || workspace?.defaultRef
10103
+ workspace?.defaultRef || workspace?.repoRef
10089
10104
  );
10090
- if (normalizedBaseRef) {
10091
- workspaceStrategy.baseRef = normalizedBaseRef;
10105
+ if (resolvedBaseRef) {
10106
+ workspaceStrategy.baseRef = resolvedBaseRef;
10092
10107
  }
10093
- return { ...policy, workspaceStrategy };
10108
+ return { ...policy, enabled: policy.enabled ?? true, workspaceStrategy };
10094
10109
  }
10095
10110
  return policy;
10096
10111
  };
@@ -10110,24 +10125,34 @@ Read: \`docs/${doc}\`
10110
10125
  };
10111
10126
  const renderDeferredIsolationNote = (workspace) => {
10112
10127
  if (!enableIsolatedWorktrees || !isFreshLocalRepo(workspace)) return "";
10113
- return `> **Enable isolated worktrees once the repo exists.** This instance has isolated worktrees enabled, but this project starts as a fresh local repository, so the \`executionWorkspacePolicy\` is intentionally omitted now \u2014 worktrees need an existing base ref and would fail on the first run. As the final step of **"Prepare GitHub repository"**, after the initial commit is pushed to \`main\`, switch this project to isolated worktrees (Project settings \u2192 isolated workspaces, or set \`executionWorkspacePolicy: { defaultMode: "isolated_workspace", workspaceStrategy: { type: "git_worktree", baseRef: "main" } }\` on the project). Until then agents share the project workspace; do not flip it before the repo has its first commit.
10128
+ const configuredRef = normalizeExecutionBaseRef(
10129
+ null,
10130
+ workspace?.defaultRef || workspace?.repoRef
10131
+ );
10132
+ const refHint = configuredRef ? `When you later enable the project policy, use that configured ref (currently \`${configuredRef}\`) as the worktree \`baseRef\`.` : `When you later enable the project policy, first set the project/worktree base ref to the branch Paperclip should branch from.`;
10133
+ return `> **Enable isolated worktrees once the repo exists.** This instance has isolated worktrees enabled, but this project starts as a fresh local repository, so the \`executionWorkspacePolicy\` is intentionally omitted now \u2014 worktrees need an existing base ref and would fail on the first run. After the initial commit exists on the configured base branch, switch this project to isolated worktrees in Project settings. ${refHint} Until then agents share the project workspace; do not flip it before the repo has its first commit.
10114
10134
 
10115
10135
  `;
10116
10136
  };
10117
10137
  const mainProject = resolvedProjects[0];
10118
10138
  const mainProjectName = mainProject?.name || companyName;
10119
- const mainProjectInfo = mainProject ? {
10120
- name: mainProjectName,
10121
- description: mainProject.description || "",
10122
- workspace: normalizeProjectWorkspace(mainProject)
10123
- } : null;
10139
+ const mainProjectInfo = mainProject ? (() => {
10140
+ const workspace = normalizeProjectWorkspace(mainProject);
10141
+ const executionWorkspacePolicy = effectiveExecutionPolicy(mainProject, workspace);
10142
+ return {
10143
+ name: mainProjectName,
10144
+ description: mainProject.description || "",
10145
+ workspace,
10146
+ ...executionWorkspacePolicy ? { executionWorkspacePolicy } : {}
10147
+ };
10148
+ })() : null;
10124
10149
  const mainProjectPreCreated = initialRoutines.length > 0 && mainProjectInfo !== null;
10125
10150
  if (resolvedProjects.length > 0) {
10126
10151
  bootstrap += `## Projects
10127
10152
 
10128
10153
  `;
10129
10154
  if (mainProjectPreCreated) {
10130
- bootstrap += `> **The Company Wizard has already created the main project "${mainProjectName}"** (with board authority) so the scheduled routines could be linked to it. Do NOT recreate it \u2014 create issues against it, and link the goals above to it.
10155
+ bootstrap += `> **The Company Wizard has already created the main project "${mainProjectName}"** (with board authority) so the scheduled routines could be linked to it. Do NOT recreate it \u2014 create issues against it. After creating the goals above, resolve their real ids and link them with \`PATCH /api/projects/{projectId}\` using \`{ "goalIds": [...] }\`.
10131
10156
 
10132
10157
  `;
10133
10158
  }
@@ -10274,7 +10299,8 @@ Read: \`docs/${doc}\`
10274
10299
  bootstrap += `- Do not reuse parent workspaces for subissues unless explicitly requested.
10275
10300
  `;
10276
10301
  if (moduleNames.includes("pr-review")) {
10277
- bootstrap += `- Required PR reviews use the issue's \`executionPolicy\`: a \`review\` stage for the Code Reviewer (plus any relevant domain reviewer \u2014 QA/UI/UX/DevOps), an \`approval\` stage for the Product Owner, then a final \`approval\` merge-gate stage for the Engineer (who merges the PR before recording approval, which closes the issue). The merge gate must be last so the Product Owner's approval does not auto-close the issue with the PR still open. Resolve each role to its agentId. Do not create separate child review issues and do not use @-mentions.
10302
+ const ciClause = hasCi ? "CI (lint/test/build) must be green before the Engineer merges \u2014 this is the hard gate and cannot be skipped" : "no CI is configured, so the Engineer must run the test suite/build and paste the real output into the merge-gate verdict before merging \u2014 this is the hard gate";
10303
+ 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 Engineer (who satisfies the hard gate above, merges the PR, then records approval to close the issue). The merge gate must be last so the Product Owner's approval does not auto-close the issue with the PR still open. The Code Reviewer and 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.
10278
10304
  `;
10279
10305
  }
10280
10306
  bootstrap += `
@@ -10308,7 +10334,7 @@ Read: \`docs/${doc}\`
10308
10334
  bootstrap += `## Provisioning Steps
10309
10335
 
10310
10336
  `;
10311
- bootstrap += `The Company Wizard "Provision" step creates the company, the CEO, and this bootstrap issue. The CEO heartbeat creates the remaining Paperclip objects below by following this task.
10337
+ bootstrap += `The Company Wizard "Provision" step creates the company, board-operations issue, hiring-plan issue, agent hire requests, routines, and this bootstrap issue. Approve any pending hires before running dependent team workflows. The CEO heartbeat completes the remaining setup below by following this task.
10312
10338
 
10313
10339
  `;
10314
10340
  bootstrap += `Manual setup order (respects Paperclip object dependencies):
@@ -10329,7 +10355,7 @@ Read: \`docs/${doc}\`
10329
10355
  const activePolicy = effectiveExecutionPolicy(proj, workspace);
10330
10356
  const policy = activePolicy?.defaultMode ? `, executionWorkspacePolicy.defaultMode: "${activePolicy.defaultMode}"` : "";
10331
10357
  if (idx === 0 && mainProjectPreCreated) {
10332
- const goalLinkInstruction = goalLinks ? ` After creating the goals above, link them to it (${goalLinks.replace(/^, /, "")}).` : "";
10358
+ const goalLinkInstruction = goalLinks ? ` After creating the goals above, resolve their real ids and link them with PATCH /api/projects/{projectId} (${goalLinks.replace(/^, /, "")}).` : "";
10333
10359
  bootstrap += `${stepN++}. **Main project already created** \u2014 the Company Wizard provisioned project "${proj.name}" (with board authority) so the scheduled routines could be linked to it. Do NOT recreate it.${goalLinkInstruction}
10334
10360
  `;
10335
10361
  } else {
@@ -10365,6 +10391,22 @@ Read: \`docs/${doc}\`
10365
10391
  };
10366
10392
  }
10367
10393
 
10394
+ // src/logic/version.js
10395
+ function compareSemver(a, b) {
10396
+ const parse = (value) => String(value || "").trim().replace(/^v/i, "").split("-", 1)[0].split(".").map((part) => Number.parseInt(part, 10) || 0);
10397
+ const left = parse(a);
10398
+ const right = parse(b);
10399
+ const max = Math.max(left.length, right.length, 3);
10400
+ for (let i = 0; i < max; i += 1) {
10401
+ const diff = (left[i] || 0) - (right[i] || 0);
10402
+ if (diff !== 0) return diff > 0 ? 1 : -1;
10403
+ }
10404
+ return 0;
10405
+ }
10406
+ function isNewerVersion(candidate, current) {
10407
+ return compareSemver(candidate, current) > 0;
10408
+ }
10409
+
10368
10410
  // src/api/client.js
10369
10411
  var BASE_ROLE_MAP = {
10370
10412
  ceo: "ceo",
@@ -10547,7 +10589,9 @@ var PaperclipClient = class {
10547
10589
  budgetMonthlyCents,
10548
10590
  permissions,
10549
10591
  metadata,
10550
- instructionsBundle
10592
+ instructionsBundle,
10593
+ sourceIssueId,
10594
+ sourceIssueIds
10551
10595
  } = agent || {};
10552
10596
  const payload = {
10553
10597
  name,
@@ -10564,49 +10608,28 @@ var PaperclipClient = class {
10564
10608
  ...budgetMonthlyCents !== void 0 ? { budgetMonthlyCents } : {},
10565
10609
  ...permissions ? { permissions } : {},
10566
10610
  ...metadata !== void 0 ? { metadata } : {},
10567
- ...instructionsBundle !== void 0 ? { instructionsBundle } : {}
10611
+ ...instructionsBundle !== void 0 ? { instructionsBundle } : {},
10612
+ ...sourceIssueId !== void 0 ? { sourceIssueId } : {},
10613
+ ...sourceIssueIds !== void 0 ? { sourceIssueIds } : {}
10568
10614
  };
10569
- try {
10570
- return await this._fetch(`/api/companies/${companyId}/agents`, {
10571
- method: "POST",
10572
- body: JSON.stringify(payload)
10573
- });
10574
- } catch (err) {
10575
- const message = err instanceof Error ? err.message : String(err);
10576
- const requiresApproval = message.includes("Direct agent creation requires board approval") || message.includes("/agent-hires");
10577
- if (!requiresApproval) throw err;
10578
- const hireResult = await this._fetch(`/api/companies/${companyId}/agent-hires`, {
10579
- method: "POST",
10580
- body: JSON.stringify(payload)
10581
- });
10582
- const hiredAgent = hireResult?.agent || null;
10583
- const approvalId = hireResult?.approval?.id || null;
10584
- if (!hiredAgent) {
10585
- throw new Error("Agent hire endpoint returned no agent.");
10586
- }
10587
- if (approvalId) {
10588
- try {
10589
- await this._fetch(`/api/approvals/${approvalId}/approve`, {
10590
- method: "POST",
10591
- body: JSON.stringify({ decisionNote: "Auto-approved by Company Wizard provisioning" })
10592
- });
10593
- try {
10594
- return await this._fetch(`/api/agents/${hiredAgent.id}`, { method: "GET" });
10595
- } catch {
10596
- return hiredAgent;
10597
- }
10598
- } catch (approveErr) {
10599
- return {
10600
- ...hiredAgent,
10601
- _pendingApprovalId: approvalId,
10602
- _approvalAutoApproveError: approveErr instanceof Error ? approveErr.message : String(approveErr)
10603
- };
10604
- }
10605
- }
10606
- return hiredAgent;
10615
+ const hireResult = await this._fetch(`/api/companies/${companyId}/agent-hires`, {
10616
+ method: "POST",
10617
+ body: JSON.stringify(payload)
10618
+ });
10619
+ const hiredAgent = hireResult?.agent || null;
10620
+ const approvalId = hireResult?.approval?.id || null;
10621
+ if (!hiredAgent) {
10622
+ throw new Error("Agent hire endpoint returned no agent.");
10607
10623
  }
10624
+ if (approvalId) {
10625
+ return {
10626
+ ...hiredAgent,
10627
+ _pendingApprovalId: approvalId
10628
+ };
10629
+ }
10630
+ return hiredAgent;
10608
10631
  }
10609
- async createProject(companyId, { name, description, goalIds, workspace }) {
10632
+ async createProject(companyId, { name, description, goalIds, workspace, executionWorkspacePolicy }) {
10610
10633
  const workspacePayload = typeof workspace === "string" ? { sourceType: "local_path", cwd: workspace, isPrimary: true } : workspace || void 0;
10611
10634
  return this._fetch(`/api/companies/${companyId}/projects`, {
10612
10635
  method: "POST",
@@ -10614,10 +10637,17 @@ var PaperclipClient = class {
10614
10637
  name,
10615
10638
  description: description || null,
10616
10639
  ...goalIds?.length ? { goalIds } : {},
10617
- workspace: workspacePayload
10640
+ workspace: workspacePayload,
10641
+ ...executionWorkspacePolicy ? { executionWorkspacePolicy } : {}
10618
10642
  })
10619
10643
  });
10620
10644
  }
10645
+ async updateProject(projectId, updates = {}) {
10646
+ return this._fetch(`/api/projects/${projectId}`, {
10647
+ method: "PATCH",
10648
+ body: JSON.stringify(updates || {})
10649
+ });
10650
+ }
10621
10651
  async createGoal(companyId, { title, description, level, parentId, status, ownerAgentId }) {
10622
10652
  return this._fetch(`/api/companies/${companyId}/goals`, {
10623
10653
  method: "POST",
@@ -10677,6 +10707,17 @@ var PaperclipClient = class {
10677
10707
  body: JSON.stringify(updates || {})
10678
10708
  });
10679
10709
  }
10710
+ async putIssueDocument(issueId, key, { title, format, body, baseRevisionId }) {
10711
+ return this._fetch(`/api/issues/${issueId}/documents/${encodeURIComponent(key)}`, {
10712
+ method: "PUT",
10713
+ body: JSON.stringify({
10714
+ title,
10715
+ format: format || "markdown",
10716
+ body: body || "",
10717
+ ...baseRevisionId !== void 0 ? { baseRevisionId } : {}
10718
+ })
10719
+ });
10720
+ }
10680
10721
  async createRoutine(companyId, {
10681
10722
  title,
10682
10723
  description,
@@ -10897,6 +10938,9 @@ function collectPresetBootstrapData(preset) {
10897
10938
  var __dirname = path2.dirname(fileURLToPath2(import.meta.url));
10898
10939
  var DEFAULT_TEMPLATES_REPO_URL = "https://github.com/starlein/paperclip-plugin-company-wizard/tree/main/templates";
10899
10940
  var BUNDLED_TEMPLATES_DIR = path2.resolve(__dirname, "..", "templates");
10941
+ var PLUGIN_PACKAGE_NAME = "@starlein/paperclip-plugin-company-wizard";
10942
+ var CURRENT_PLUGIN_VERSION = "0.4.1";
10943
+ var NPM_LATEST_URL = "https://registry.npmjs.org/@starlein%2Fpaperclip-plugin-company-wizard/latest";
10900
10944
  function copyDirSync(src, dest) {
10901
10945
  fs2.mkdirSync(dest, { recursive: true });
10902
10946
  for (const entry of fs2.readdirSync(src, { withFileTypes: true })) {
@@ -11090,6 +11134,49 @@ function resolveCompaniesDir(cfg) {
11090
11134
  if (cfg.companiesDir) return cfg.companiesDir;
11091
11135
  return path2.join(os.homedir(), ".paperclip", "instances", "default", "companies");
11092
11136
  }
11137
+ function mkdirErrorMessage(err) {
11138
+ if (err instanceof Error) return err.message;
11139
+ return String(err);
11140
+ }
11141
+ function ensureWritableDir(dir) {
11142
+ fs2.mkdirSync(dir, { recursive: true });
11143
+ fs2.accessSync(dir, fs2.constants.W_OK);
11144
+ }
11145
+ function resolveWritableCompaniesDir(cfg, log) {
11146
+ const configuredDir = typeof cfg.companiesDir === "string" ? cfg.companiesDir.trim() : "";
11147
+ if (configuredDir) {
11148
+ try {
11149
+ ensureWritableDir(configuredDir);
11150
+ return configuredDir;
11151
+ } catch (err) {
11152
+ throw new Error(
11153
+ `Configured companiesDir is not writable (${configuredDir}): ${mkdirErrorMessage(err)}`
11154
+ );
11155
+ }
11156
+ }
11157
+ const candidates = [
11158
+ resolveCompaniesDir(cfg),
11159
+ path2.join("/paperclip", "instances", "default", "companies"),
11160
+ path2.join(os.tmpdir(), "paperclip-companies")
11161
+ ];
11162
+ const attempted = /* @__PURE__ */ new Set();
11163
+ let lastError = "";
11164
+ for (const candidate of candidates) {
11165
+ if (attempted.has(candidate)) continue;
11166
+ attempted.add(candidate);
11167
+ try {
11168
+ ensureWritableDir(candidate);
11169
+ return candidate;
11170
+ } catch (err) {
11171
+ const message = mkdirErrorMessage(err);
11172
+ lastError = `${candidate}: ${message}`;
11173
+ if (log) log(`\u26A0 Companies dir unavailable: ${candidate} (${message})`);
11174
+ }
11175
+ }
11176
+ throw new Error(
11177
+ `Unable to prepare a writable companies directory. Last attempt failed at ${lastError}`
11178
+ );
11179
+ }
11093
11180
  function formatRoleName(role) {
11094
11181
  return role.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
11095
11182
  }
@@ -11147,6 +11234,57 @@ async function syncAgentInstructionsIntoManagedBundle({
11147
11234
  );
11148
11235
  }
11149
11236
  }
11237
+ function buildDecisionLogBody({
11238
+ companyName,
11239
+ companyDescription,
11240
+ preset,
11241
+ moduleNames,
11242
+ roles,
11243
+ repositoryMode,
11244
+ approvalMode
11245
+ }) {
11246
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
11247
+ return `# Decision Log \u2014 ${companyName}
11248
+
11249
+ ## ${today}
11250
+ - Created or selected company "${companyName}"${companyDescription ? ` with mission: ${companyDescription}` : ""}.
11251
+ - Selected preset: ${preset || "custom/manual"}.
11252
+ - Selected modules: ${moduleNames.length > 0 ? moduleNames.join(", ") : "none"}.
11253
+ - Initial roles: ${roles.join(", ")}.
11254
+ - Repository setup: ${repositoryMode}.
11255
+ - Hiring governance: ${approvalMode}.
11256
+ - Company Wizard generated the initial bootstrap package, agent instruction bundles, routines, and backlog seed from templates.
11257
+ `;
11258
+ }
11259
+ function buildHiringPlanBody({
11260
+ companyName,
11261
+ roles,
11262
+ moduleNames
11263
+ }) {
11264
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
11265
+ return `# Hiring Plan \u2014 ${companyName}
11266
+
11267
+ Generated by Company Wizard on ${today}.
11268
+
11269
+ ## Instruction Source
11270
+
11271
+ Company Wizard uses curated role templates from its template library. When proposing additional hires after bootstrap, follow the Paperclip \`paperclip-create-agent\` workflow: choose an exact, adjacent, or generic template; run the draft-review checklist; submit via \`/agent-hires\`; link this issue as \`sourceIssueId\`; and wait for board approval when required.
11272
+
11273
+ ## Initial Roles
11274
+
11275
+ ${roles.map((role) => `- ${formatRoleName(role)}`).join("\n")}
11276
+
11277
+ ## Selected Modules
11278
+
11279
+ ${moduleNames.length > 0 ? moduleNames.map((mod) => `- ${mod}`).join("\n") : "- none"}
11280
+
11281
+ ## Follow-up Review
11282
+
11283
+ - Reassess capacity after the first roadmap/backlog pass.
11284
+ - Do not hire for capabilities already covered by existing roles.
11285
+ - If a gap remains, submit a governed hire request with a concrete AGENTS.md draft, adapter config, desiredSkills justification, and sourceIssueId pointing to this hiring plan issue.
11286
+ `;
11287
+ }
11150
11288
  var plugin = definePlugin({
11151
11289
  async setup(ctx) {
11152
11290
  ctx.data.register("templates", async () => {
@@ -11173,6 +11311,46 @@ var plugin = definePlugin({
11173
11311
  return { ok: false, error: err instanceof Error ? err.message : String(err) };
11174
11312
  }
11175
11313
  });
11314
+ ctx.actions.register("check-update", async () => {
11315
+ try {
11316
+ const response = await fetch(NPM_LATEST_URL, {
11317
+ headers: { accept: "application/json" }
11318
+ });
11319
+ if (!response.ok) {
11320
+ return {
11321
+ ok: false,
11322
+ currentVersion: CURRENT_PLUGIN_VERSION,
11323
+ packageName: PLUGIN_PACKAGE_NAME,
11324
+ error: `npm registry returned ${response.status}`
11325
+ };
11326
+ }
11327
+ const data = await response.json();
11328
+ const latestVersion = typeof data.version === "string" ? data.version.trim() : "";
11329
+ if (!latestVersion) {
11330
+ return {
11331
+ ok: false,
11332
+ currentVersion: CURRENT_PLUGIN_VERSION,
11333
+ packageName: PLUGIN_PACKAGE_NAME,
11334
+ error: "npm registry response did not include a version"
11335
+ };
11336
+ }
11337
+ return {
11338
+ ok: true,
11339
+ packageName: PLUGIN_PACKAGE_NAME,
11340
+ currentVersion: CURRENT_PLUGIN_VERSION,
11341
+ latestVersion,
11342
+ updateAvailable: isNewerVersion(latestVersion, CURRENT_PLUGIN_VERSION),
11343
+ url: "https://www.npmjs.com/package/@starlein/paperclip-plugin-company-wizard"
11344
+ };
11345
+ } catch (err) {
11346
+ return {
11347
+ ok: false,
11348
+ currentVersion: CURRENT_PLUGIN_VERSION,
11349
+ packageName: PLUGIN_PACKAGE_NAME,
11350
+ error: err instanceof Error ? err.message : String(err)
11351
+ };
11352
+ }
11353
+ });
11176
11354
  ctx.actions.register("preview-files", async (params) => {
11177
11355
  let tmpDir;
11178
11356
  try {
@@ -11197,7 +11375,7 @@ var plugin = definePlugin({
11197
11375
  const companyName = typeof params.companyName === "string" && params.companyName.trim() ? params.companyName.trim() : "Preview";
11198
11376
  const templatesDir = await ensureTemplatesDir(cfg);
11199
11377
  tmpDir = path2.join(os.tmpdir(), `company-wizard-preview-${Date.now()}`);
11200
- const companiesDir = resolveCompaniesDir(cfg);
11378
+ const companiesDir = resolveWritableCompaniesDir(cfg);
11201
11379
  const [presets, allModules] = await Promise.all([
11202
11380
  loadPresets(templatesDir),
11203
11381
  loadModules(templatesDir)
@@ -11225,7 +11403,7 @@ var plugin = definePlugin({
11225
11403
  presetRoutines: presetBootstrapData.routines,
11226
11404
  presetLabels: presetBootstrapData.labels,
11227
11405
  enableIsolatedWorktrees: await resolveEnableIsolatedWorkspacesFromInstance(cfg),
11228
- enableEnrichedPersonas: cfgBool(cfg, "enableEnrichedPersonas"),
11406
+ enableEnrichedPersonas: true,
11229
11407
  outputDir: tmpDir,
11230
11408
  templatesDir
11231
11409
  });
@@ -11331,7 +11509,7 @@ var plugin = definePlugin({
11331
11509
  "disableBoardApprovalOnNewCompanies"
11332
11510
  );
11333
11511
  const enableIsolatedWorktrees = await resolveEnableIsolatedWorkspacesFromInstance(cfg, log);
11334
- const enableEnrichedPersonas = cfgBool(cfg, "enableEnrichedPersonas");
11512
+ const enableEnrichedPersonas = true;
11335
11513
  const companyName = typeof params.companyName === "string" ? params.companyName.trim() : "";
11336
11514
  const existingCompanyId = typeof params.existingCompanyId === "string" && params.existingCompanyId.trim() ? params.existingCompanyId.trim() : "";
11337
11515
  if (!companyName) return { error: "companyName is required", logs };
@@ -11352,8 +11530,7 @@ var plugin = definePlugin({
11352
11530
  );
11353
11531
  const goals = collectGoals(selectedPreset, allModules, new Set(effectiveModules));
11354
11532
  const presetBootstrapData = collectPresetBootstrapData(selectedPreset);
11355
- const outputDir = resolveCompaniesDir(cfg);
11356
- fs2.mkdirSync(outputDir, { recursive: true });
11533
+ const outputDir = resolveWritableCompaniesDir(cfg, log);
11357
11534
  log("Assembling company workspace...");
11358
11535
  const companyDescription = typeof params.companyDescription === "string" ? params.companyDescription.trim() : "";
11359
11536
  const userGoals = Array.isArray(params.goals) ? params.goals : params.goal ? [params.goal] : [];
@@ -11438,8 +11615,58 @@ var plugin = definePlugin({
11438
11615
  }
11439
11616
  let ceoAgentId;
11440
11617
  const teamAgentIds = {};
11618
+ let boardOperationsIssue = null;
11619
+ let hiringPlanIssue = null;
11441
11620
  let bootstrapIssue;
11442
11621
  try {
11622
+ const allRoleNames = [...assembleResult.allRoles ?? []].filter(Boolean).sort();
11623
+ const repositoryMode = userProjects.some(
11624
+ (project) => project?.repoUrl || project?.workspace?.sourceType === "git_repo"
11625
+ ) ? "existing git repository" : "fresh local repository";
11626
+ const approvalMode = disableBoardApprovalOnNewCompanies ? "board approval disabled for this new company by plugin setting" : "board approval preserved; /agent-hires may create pending approvals";
11627
+ log("Creating board operations and hiring plan records...");
11628
+ const createdBoardOperationsIssue = await client.createIssue(companyId, {
11629
+ title: "Board Operations",
11630
+ description: "Standing issue for board decision log and operations tracking.",
11631
+ status: "in_progress",
11632
+ priority: "medium"
11633
+ });
11634
+ boardOperationsIssue = createdBoardOperationsIssue;
11635
+ await client.putIssueDocument(createdBoardOperationsIssue.id, "decision-log", {
11636
+ title: "Decision Log",
11637
+ format: "markdown",
11638
+ body: buildDecisionLogBody({
11639
+ companyName,
11640
+ companyDescription,
11641
+ preset: selectedPreset?.name,
11642
+ moduleNames: effectiveModules,
11643
+ roles: allRoleNames,
11644
+ repositoryMode,
11645
+ approvalMode
11646
+ })
11647
+ });
11648
+ log(
11649
+ `\u2713 Board Operations issue created${createdBoardOperationsIssue.identifier ? `: ${createdBoardOperationsIssue.identifier}` : ""}`
11650
+ );
11651
+ const createdHiringPlanIssue = await client.createIssue(companyId, {
11652
+ title: "Hiring Plan",
11653
+ description: "Develop and execute the governed team hiring plan.",
11654
+ status: "in_progress",
11655
+ priority: "high"
11656
+ });
11657
+ hiringPlanIssue = createdHiringPlanIssue;
11658
+ await client.putIssueDocument(createdHiringPlanIssue.id, "hiring-plan", {
11659
+ title: "Hiring Plan",
11660
+ format: "markdown",
11661
+ body: buildHiringPlanBody({
11662
+ companyName,
11663
+ roles: allRoleNames,
11664
+ moduleNames: effectiveModules
11665
+ })
11666
+ });
11667
+ log(
11668
+ `\u2713 Hiring Plan issue created${createdHiringPlanIssue.identifier ? `: ${createdHiringPlanIssue.identifier}` : ""}`
11669
+ );
11443
11670
  const userCeoAdapter = params.ceoAdapter || {};
11444
11671
  const adapterType = normalizeCeoAdapterType(userCeoAdapter);
11445
11672
  const ceoTemplate = roleTemplateByName.get("ceo") || {};
@@ -11465,9 +11692,6 @@ var plugin = definePlugin({
11465
11692
  log(
11466
11693
  `\u26A0 CEO hire is pending approval: ${agent._pendingApprovalId}. Approve it in the board before the bootstrap heartbeat can run.`
11467
11694
  );
11468
- if (agent._approvalAutoApproveError) {
11469
- log(` Auto-approve failed: ${agent._approvalAutoApproveError}`);
11470
- }
11471
11695
  };
11472
11696
  if (existingCompanyId) {
11473
11697
  log("Looking for an existing CEO agent...");
@@ -11512,7 +11736,8 @@ var plugin = definePlugin({
11512
11736
  adapterConfig,
11513
11737
  instructionsBundle: ceoInstructionsBundle,
11514
11738
  runtimeConfig: ceoRuntimeConfig,
11515
- permissions: { canCreateAgents: true }
11739
+ permissions: { canCreateAgents: true },
11740
+ ...boardOperationsIssue?.id ? { sourceIssueId: boardOperationsIssue.id } : {}
11516
11741
  });
11517
11742
  ceoAgentId = ceoAgent.id;
11518
11743
  log(`\u2713 CEO agent created (${ceoAgentId})`);
@@ -11531,7 +11756,8 @@ var plugin = definePlugin({
11531
11756
  adapterConfig,
11532
11757
  instructionsBundle: ceoInstructionsBundle,
11533
11758
  runtimeConfig: ceoRuntimeConfig,
11534
- permissions: { canCreateAgents: true }
11759
+ permissions: { canCreateAgents: true },
11760
+ ...boardOperationsIssue?.id ? { sourceIssueId: boardOperationsIssue.id } : {}
11535
11761
  });
11536
11762
  ceoAgentId = ceoAgent.id;
11537
11763
  log(`\u2713 CEO agent created (${ceoAgentId})`);
@@ -11608,7 +11834,8 @@ var plugin = definePlugin({
11608
11834
  adapterType,
11609
11835
  adapterConfig: roleAdapterConfig,
11610
11836
  instructionsBundle: roleInstructionsBundle,
11611
- runtimeConfig: roleRuntimeConfig
11837
+ runtimeConfig: roleRuntimeConfig,
11838
+ ...hiringPlanIssue?.id ? { sourceIssueId: hiringPlanIssue.id } : {}
11612
11839
  });
11613
11840
  teamAgentIds[roleName] = roleAgent.id;
11614
11841
  log(`\u2713 ${roleTitle} created (${roleAgent.id})`);
@@ -11616,9 +11843,6 @@ var plugin = definePlugin({
11616
11843
  log(
11617
11844
  `\u26A0 ${roleTitle} hire pending approval: ${roleAgent._pendingApprovalId}. Approve it in the board.`
11618
11845
  );
11619
- if (roleAgent._approvalAutoApproveError) {
11620
- log(` Auto-approve failed: ${roleAgent._approvalAutoApproveError}`);
11621
- }
11622
11846
  }
11623
11847
  }
11624
11848
  if (!existingCompanyId) {
@@ -11630,7 +11854,8 @@ var plugin = definePlugin({
11630
11854
  const createdProject = await client.createProject(companyId, {
11631
11855
  name: mainProject.name,
11632
11856
  description: mainProject.description,
11633
- workspace: mainProject.workspace
11857
+ workspace: mainProject.workspace,
11858
+ executionWorkspacePolicy: mainProject.executionWorkspacePolicy
11634
11859
  });
11635
11860
  mainProjectId = createdProject?.id;
11636
11861
  log(
@@ -11707,14 +11932,17 @@ var plugin = definePlugin({
11707
11932
  log("");
11708
11933
  log("Provisioning complete!");
11709
11934
  log(
11710
- existingCompanyId ? "Bootstrap task created in existing company. Trigger the CEO heartbeat to continue setup." : "The CEO agent is ready. Trigger its first heartbeat to hire the rest of the team and create the initial backlog."
11935
+ existingCompanyId ? "Bootstrap task created in existing company. Trigger the CEO heartbeat to continue setup." : "The CEO agent and initial team hire requests are ready. Approve any pending hires, then trigger the CEO heartbeat to continue setup."
11936
+ );
11937
+ const issueIds = [boardOperationsIssue?.id, hiringPlanIssue?.id, bootstrapIssue.id].filter(
11938
+ Boolean
11711
11939
  );
11712
11940
  return {
11713
11941
  companyId,
11714
11942
  issuePrefix: company.issuePrefix,
11715
11943
  paperclipUrl,
11716
11944
  agentIds: { ceo: ceoAgentId, ...teamAgentIds },
11717
- issueIds: [bootstrapIssue.id],
11945
+ issueIds,
11718
11946
  logs
11719
11947
  };
11720
11948
  } catch (err) {
@@ -11750,6 +11978,7 @@ var plugin = definePlugin({
11750
11978
  var worker_default = plugin;
11751
11979
  runWorker(plugin, import.meta.url);
11752
11980
  export {
11753
- worker_default as default
11981
+ worker_default as default,
11982
+ resolveWritableCompaniesDir
11754
11983
  };
11755
11984
  //# sourceMappingURL=worker.js.map