@starlein/paperclip-plugin-company-wizard 0.3.24 → 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.
- package/CHANGELOG.md +24 -0
- package/README.md +15 -11
- package/dist/manifest.js +1 -6
- package/dist/manifest.js.map +2 -2
- package/dist/ui/index.css +3 -0
- package/dist/ui/index.css.map +2 -2
- package/dist/ui/index.js +59 -30
- package/dist/ui/index.js.map +3 -3
- package/dist/worker.js +263 -81
- package/dist/worker.js.map +3 -3
- package/package.json +1 -1
- package/templates/ai-wizard/config-format.md +4 -4
- package/templates/ai-wizard/interview-system.md +3 -3
- package/templates/ai-wizard/single-shot-system.md +3 -3
- package/templates/bootstrap-instructions.md +3 -3
- package/templates/modules/architecture-plan/agents/ui-designer/skills/design-system.md +1 -1
- package/templates/modules/auto-assign/agents/ceo/heartbeat-section.md +2 -8
- package/templates/modules/auto-assign/agents/product-owner/heartbeat-section.md +2 -9
- package/templates/modules/auto-assign/module.meta.json +1 -1
- package/templates/modules/auto-assign/skills/auto-assign.md +18 -15
- package/templates/modules/backlog/agents/ceo/heartbeat-section.md +2 -9
- package/templates/modules/backlog/agents/product-owner/heartbeat-section.md +2 -14
- package/templates/modules/backlog/module.meta.json +1 -1
- package/templates/modules/backlog/skills/backlog-health.md +20 -21
- package/templates/modules/codebase-onboarding/skills/codebase-audit.md +29 -30
- package/templates/modules/github-repo/agents/engineer/skills/git-workflow.md +11 -7
- package/templates/modules/github-repo/docs/git-workflow.md +10 -6
- package/templates/modules/hiring-review/skills/hiring-review.md +40 -16
- package/templates/modules/market-analysis/agents/ux-researcher/skills/market-analysis.md +1 -1
- package/templates/modules/pr-review/README.md +13 -13
- package/templates/modules/pr-review/agents/devops/skills/infra-review.md +1 -1
- package/templates/modules/pr-review/agents/engineer/skills/pr-workflow.md +17 -11
- package/templates/modules/pr-review/agents/product-owner/skills/product-review.md +1 -1
- package/templates/modules/pr-review/agents/qa/skills/qa-review.md +1 -1
- package/templates/modules/pr-review/agents/security-engineer/skills/pr-security-review.md +1 -1
- package/templates/modules/pr-review/agents/ui-designer/skills/design-review.md +1 -1
- package/templates/modules/pr-review/agents/ux-researcher/skills/ux-review.md +1 -1
- package/templates/modules/pr-review/docs/pr-conventions.md +11 -8
- package/templates/modules/stall-detection/README.md +8 -8
- package/templates/modules/stall-detection/agents/ceo/heartbeat-section.md +2 -11
- package/templates/modules/stall-detection/agents/ceo/skills/stall-detection.md +22 -13
- package/templates/modules/stall-detection/module.meta.json +1 -1
- package/templates/modules/triage/skills/issue-triage.md +20 -33
- package/templates/roles/audio-designer/HEARTBEAT.md +37 -21
- package/templates/roles/ceo/HEARTBEAT.md +34 -56
- package/templates/roles/cmo/HEARTBEAT.md +37 -21
- package/templates/roles/code-reviewer/HEARTBEAT.md +39 -19
- package/templates/roles/cto/HEARTBEAT.md +33 -25
- package/templates/roles/customer-success/HEARTBEAT.md +39 -19
- package/templates/roles/devops/HEARTBEAT.md +36 -25
- package/templates/roles/engineer/AGENTS.md +27 -9
- package/templates/roles/engineer/HEARTBEAT.md +35 -21
- package/templates/roles/game-artist/HEARTBEAT.md +37 -21
- package/templates/roles/game-designer/HEARTBEAT.md +37 -21
- package/templates/roles/level-designer/HEARTBEAT.md +37 -21
- package/templates/roles/product-owner/AGENTS.md +24 -8
- package/templates/roles/product-owner/HEARTBEAT.md +37 -19
- package/templates/roles/qa/AGENTS.md +26 -11
- package/templates/roles/qa/HEARTBEAT.md +37 -21
- package/templates/roles/security-engineer/AGENTS.md +21 -23
- package/templates/roles/security-engineer/HEARTBEAT.md +39 -19
- package/templates/roles/technical-writer/HEARTBEAT.md +39 -18
- package/templates/roles/ui-designer/AGENTS.md +26 -9
- package/templates/roles/ui-designer/HEARTBEAT.md +37 -21
- 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
|
-
|
|
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 =
|
|
9399
|
+
enableEnrichedPersonas = true,
|
|
9404
9400
|
outputDir,
|
|
9405
9401
|
templatesDir,
|
|
9406
9402
|
onProgress = () => {
|
|
@@ -10013,6 +10009,9 @@ Read: \`docs/${doc}\`
|
|
|
10013
10009
|
if (allGoals.length > 0) {
|
|
10014
10010
|
bootstrap += `## Goals
|
|
10015
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
|
+
|
|
10016
10015
|
`;
|
|
10017
10016
|
for (const g of allGoals) {
|
|
10018
10017
|
bootstrap += `### ${g.title}
|
|
@@ -10079,21 +10078,34 @@ Read: \`docs/${doc}\`
|
|
|
10079
10078
|
const isFreshLocalRepo = (workspace) => workspace?.sourceType !== "git_repo";
|
|
10080
10079
|
const effectiveExecutionPolicy = (proj, workspace) => {
|
|
10081
10080
|
const policy = proj?.executionWorkspacePolicy;
|
|
10082
|
-
|
|
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
|
+
}
|
|
10083
10094
|
if (policy.defaultMode === "isolated_workspace") {
|
|
10084
|
-
if (!
|
|
10095
|
+
if (!canUseIsolatedWorktrees) return null;
|
|
10085
10096
|
const strategy = policy.workspaceStrategy;
|
|
10086
|
-
if (!strategy || typeof strategy !== "object")
|
|
10087
|
-
|
|
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 };
|
|
10088
10100
|
const workspaceStrategy = { ...strategy };
|
|
10089
|
-
const
|
|
10101
|
+
const resolvedBaseRef = normalizeExecutionBaseRef(
|
|
10090
10102
|
workspaceStrategy.baseRef,
|
|
10091
|
-
workspace?.
|
|
10103
|
+
workspace?.defaultRef || workspace?.repoRef
|
|
10092
10104
|
);
|
|
10093
|
-
if (
|
|
10094
|
-
workspaceStrategy.baseRef =
|
|
10105
|
+
if (resolvedBaseRef) {
|
|
10106
|
+
workspaceStrategy.baseRef = resolvedBaseRef;
|
|
10095
10107
|
}
|
|
10096
|
-
return { ...policy, workspaceStrategy };
|
|
10108
|
+
return { ...policy, enabled: policy.enabled ?? true, workspaceStrategy };
|
|
10097
10109
|
}
|
|
10098
10110
|
return policy;
|
|
10099
10111
|
};
|
|
@@ -10113,24 +10125,34 @@ Read: \`docs/${doc}\`
|
|
|
10113
10125
|
};
|
|
10114
10126
|
const renderDeferredIsolationNote = (workspace) => {
|
|
10115
10127
|
if (!enableIsolatedWorktrees || !isFreshLocalRepo(workspace)) return "";
|
|
10116
|
-
|
|
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.
|
|
10117
10134
|
|
|
10118
10135
|
`;
|
|
10119
10136
|
};
|
|
10120
10137
|
const mainProject = resolvedProjects[0];
|
|
10121
10138
|
const mainProjectName = mainProject?.name || companyName;
|
|
10122
|
-
const mainProjectInfo = mainProject ? {
|
|
10123
|
-
|
|
10124
|
-
|
|
10125
|
-
|
|
10126
|
-
|
|
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;
|
|
10127
10149
|
const mainProjectPreCreated = initialRoutines.length > 0 && mainProjectInfo !== null;
|
|
10128
10150
|
if (resolvedProjects.length > 0) {
|
|
10129
10151
|
bootstrap += `## Projects
|
|
10130
10152
|
|
|
10131
10153
|
`;
|
|
10132
10154
|
if (mainProjectPreCreated) {
|
|
10133
|
-
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
|
|
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": [...] }\`.
|
|
10134
10156
|
|
|
10135
10157
|
`;
|
|
10136
10158
|
}
|
|
@@ -10278,7 +10300,7 @@ Read: \`docs/${doc}\`
|
|
|
10278
10300
|
`;
|
|
10279
10301
|
if (moduleNames.includes("pr-review")) {
|
|
10280
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";
|
|
10281
|
-
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.
|
|
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.
|
|
10282
10304
|
`;
|
|
10283
10305
|
}
|
|
10284
10306
|
bootstrap += `
|
|
@@ -10312,7 +10334,7 @@ Read: \`docs/${doc}\`
|
|
|
10312
10334
|
bootstrap += `## Provisioning Steps
|
|
10313
10335
|
|
|
10314
10336
|
`;
|
|
10315
|
-
bootstrap += `The Company Wizard "Provision" step creates the company,
|
|
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.
|
|
10316
10338
|
|
|
10317
10339
|
`;
|
|
10318
10340
|
bootstrap += `Manual setup order (respects Paperclip object dependencies):
|
|
@@ -10333,7 +10355,7 @@ Read: \`docs/${doc}\`
|
|
|
10333
10355
|
const activePolicy = effectiveExecutionPolicy(proj, workspace);
|
|
10334
10356
|
const policy = activePolicy?.defaultMode ? `, executionWorkspacePolicy.defaultMode: "${activePolicy.defaultMode}"` : "";
|
|
10335
10357
|
if (idx === 0 && mainProjectPreCreated) {
|
|
10336
|
-
const goalLinkInstruction = goalLinks ? ` After creating the goals above, link them
|
|
10358
|
+
const goalLinkInstruction = goalLinks ? ` After creating the goals above, resolve their real ids and link them with PATCH /api/projects/{projectId} (${goalLinks.replace(/^, /, "")}).` : "";
|
|
10337
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}
|
|
10338
10360
|
`;
|
|
10339
10361
|
} else {
|
|
@@ -10369,6 +10391,22 @@ Read: \`docs/${doc}\`
|
|
|
10369
10391
|
};
|
|
10370
10392
|
}
|
|
10371
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
|
+
|
|
10372
10410
|
// src/api/client.js
|
|
10373
10411
|
var BASE_ROLE_MAP = {
|
|
10374
10412
|
ceo: "ceo",
|
|
@@ -10551,7 +10589,9 @@ var PaperclipClient = class {
|
|
|
10551
10589
|
budgetMonthlyCents,
|
|
10552
10590
|
permissions,
|
|
10553
10591
|
metadata,
|
|
10554
|
-
instructionsBundle
|
|
10592
|
+
instructionsBundle,
|
|
10593
|
+
sourceIssueId,
|
|
10594
|
+
sourceIssueIds
|
|
10555
10595
|
} = agent || {};
|
|
10556
10596
|
const payload = {
|
|
10557
10597
|
name,
|
|
@@ -10568,49 +10608,28 @@ var PaperclipClient = class {
|
|
|
10568
10608
|
...budgetMonthlyCents !== void 0 ? { budgetMonthlyCents } : {},
|
|
10569
10609
|
...permissions ? { permissions } : {},
|
|
10570
10610
|
...metadata !== void 0 ? { metadata } : {},
|
|
10571
|
-
...instructionsBundle !== void 0 ? { instructionsBundle } : {}
|
|
10611
|
+
...instructionsBundle !== void 0 ? { instructionsBundle } : {},
|
|
10612
|
+
...sourceIssueId !== void 0 ? { sourceIssueId } : {},
|
|
10613
|
+
...sourceIssueIds !== void 0 ? { sourceIssueIds } : {}
|
|
10572
10614
|
};
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
10577
|
-
|
|
10578
|
-
|
|
10579
|
-
|
|
10580
|
-
|
|
10581
|
-
if (!requiresApproval) throw err;
|
|
10582
|
-
const hireResult = await this._fetch(`/api/companies/${companyId}/agent-hires`, {
|
|
10583
|
-
method: "POST",
|
|
10584
|
-
body: JSON.stringify(payload)
|
|
10585
|
-
});
|
|
10586
|
-
const hiredAgent = hireResult?.agent || null;
|
|
10587
|
-
const approvalId = hireResult?.approval?.id || null;
|
|
10588
|
-
if (!hiredAgent) {
|
|
10589
|
-
throw new Error("Agent hire endpoint returned no agent.");
|
|
10590
|
-
}
|
|
10591
|
-
if (approvalId) {
|
|
10592
|
-
try {
|
|
10593
|
-
await this._fetch(`/api/approvals/${approvalId}/approve`, {
|
|
10594
|
-
method: "POST",
|
|
10595
|
-
body: JSON.stringify({ decisionNote: "Auto-approved by Company Wizard provisioning" })
|
|
10596
|
-
});
|
|
10597
|
-
try {
|
|
10598
|
-
return await this._fetch(`/api/agents/${hiredAgent.id}`, { method: "GET" });
|
|
10599
|
-
} catch {
|
|
10600
|
-
return hiredAgent;
|
|
10601
|
-
}
|
|
10602
|
-
} catch (approveErr) {
|
|
10603
|
-
return {
|
|
10604
|
-
...hiredAgent,
|
|
10605
|
-
_pendingApprovalId: approvalId,
|
|
10606
|
-
_approvalAutoApproveError: approveErr instanceof Error ? approveErr.message : String(approveErr)
|
|
10607
|
-
};
|
|
10608
|
-
}
|
|
10609
|
-
}
|
|
10610
|
-
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.");
|
|
10611
10623
|
}
|
|
10624
|
+
if (approvalId) {
|
|
10625
|
+
return {
|
|
10626
|
+
...hiredAgent,
|
|
10627
|
+
_pendingApprovalId: approvalId
|
|
10628
|
+
};
|
|
10629
|
+
}
|
|
10630
|
+
return hiredAgent;
|
|
10612
10631
|
}
|
|
10613
|
-
async createProject(companyId, { name, description, goalIds, workspace }) {
|
|
10632
|
+
async createProject(companyId, { name, description, goalIds, workspace, executionWorkspacePolicy }) {
|
|
10614
10633
|
const workspacePayload = typeof workspace === "string" ? { sourceType: "local_path", cwd: workspace, isPrimary: true } : workspace || void 0;
|
|
10615
10634
|
return this._fetch(`/api/companies/${companyId}/projects`, {
|
|
10616
10635
|
method: "POST",
|
|
@@ -10618,10 +10637,17 @@ var PaperclipClient = class {
|
|
|
10618
10637
|
name,
|
|
10619
10638
|
description: description || null,
|
|
10620
10639
|
...goalIds?.length ? { goalIds } : {},
|
|
10621
|
-
workspace: workspacePayload
|
|
10640
|
+
workspace: workspacePayload,
|
|
10641
|
+
...executionWorkspacePolicy ? { executionWorkspacePolicy } : {}
|
|
10622
10642
|
})
|
|
10623
10643
|
});
|
|
10624
10644
|
}
|
|
10645
|
+
async updateProject(projectId, updates = {}) {
|
|
10646
|
+
return this._fetch(`/api/projects/${projectId}`, {
|
|
10647
|
+
method: "PATCH",
|
|
10648
|
+
body: JSON.stringify(updates || {})
|
|
10649
|
+
});
|
|
10650
|
+
}
|
|
10625
10651
|
async createGoal(companyId, { title, description, level, parentId, status, ownerAgentId }) {
|
|
10626
10652
|
return this._fetch(`/api/companies/${companyId}/goals`, {
|
|
10627
10653
|
method: "POST",
|
|
@@ -10681,6 +10707,17 @@ var PaperclipClient = class {
|
|
|
10681
10707
|
body: JSON.stringify(updates || {})
|
|
10682
10708
|
});
|
|
10683
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
|
+
}
|
|
10684
10721
|
async createRoutine(companyId, {
|
|
10685
10722
|
title,
|
|
10686
10723
|
description,
|
|
@@ -10901,6 +10938,9 @@ function collectPresetBootstrapData(preset) {
|
|
|
10901
10938
|
var __dirname = path2.dirname(fileURLToPath2(import.meta.url));
|
|
10902
10939
|
var DEFAULT_TEMPLATES_REPO_URL = "https://github.com/starlein/paperclip-plugin-company-wizard/tree/main/templates";
|
|
10903
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";
|
|
10904
10944
|
function copyDirSync(src, dest) {
|
|
10905
10945
|
fs2.mkdirSync(dest, { recursive: true });
|
|
10906
10946
|
for (const entry of fs2.readdirSync(src, { withFileTypes: true })) {
|
|
@@ -11194,6 +11234,57 @@ async function syncAgentInstructionsIntoManagedBundle({
|
|
|
11194
11234
|
);
|
|
11195
11235
|
}
|
|
11196
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
|
+
}
|
|
11197
11288
|
var plugin = definePlugin({
|
|
11198
11289
|
async setup(ctx) {
|
|
11199
11290
|
ctx.data.register("templates", async () => {
|
|
@@ -11220,6 +11311,46 @@ var plugin = definePlugin({
|
|
|
11220
11311
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
11221
11312
|
}
|
|
11222
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
|
+
});
|
|
11223
11354
|
ctx.actions.register("preview-files", async (params) => {
|
|
11224
11355
|
let tmpDir;
|
|
11225
11356
|
try {
|
|
@@ -11272,7 +11403,7 @@ var plugin = definePlugin({
|
|
|
11272
11403
|
presetRoutines: presetBootstrapData.routines,
|
|
11273
11404
|
presetLabels: presetBootstrapData.labels,
|
|
11274
11405
|
enableIsolatedWorktrees: await resolveEnableIsolatedWorkspacesFromInstance(cfg),
|
|
11275
|
-
enableEnrichedPersonas:
|
|
11406
|
+
enableEnrichedPersonas: true,
|
|
11276
11407
|
outputDir: tmpDir,
|
|
11277
11408
|
templatesDir
|
|
11278
11409
|
});
|
|
@@ -11378,7 +11509,7 @@ var plugin = definePlugin({
|
|
|
11378
11509
|
"disableBoardApprovalOnNewCompanies"
|
|
11379
11510
|
);
|
|
11380
11511
|
const enableIsolatedWorktrees = await resolveEnableIsolatedWorkspacesFromInstance(cfg, log);
|
|
11381
|
-
const enableEnrichedPersonas =
|
|
11512
|
+
const enableEnrichedPersonas = true;
|
|
11382
11513
|
const companyName = typeof params.companyName === "string" ? params.companyName.trim() : "";
|
|
11383
11514
|
const existingCompanyId = typeof params.existingCompanyId === "string" && params.existingCompanyId.trim() ? params.existingCompanyId.trim() : "";
|
|
11384
11515
|
if (!companyName) return { error: "companyName is required", logs };
|
|
@@ -11484,8 +11615,58 @@ var plugin = definePlugin({
|
|
|
11484
11615
|
}
|
|
11485
11616
|
let ceoAgentId;
|
|
11486
11617
|
const teamAgentIds = {};
|
|
11618
|
+
let boardOperationsIssue = null;
|
|
11619
|
+
let hiringPlanIssue = null;
|
|
11487
11620
|
let bootstrapIssue;
|
|
11488
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
|
+
);
|
|
11489
11670
|
const userCeoAdapter = params.ceoAdapter || {};
|
|
11490
11671
|
const adapterType = normalizeCeoAdapterType(userCeoAdapter);
|
|
11491
11672
|
const ceoTemplate = roleTemplateByName.get("ceo") || {};
|
|
@@ -11511,9 +11692,6 @@ var plugin = definePlugin({
|
|
|
11511
11692
|
log(
|
|
11512
11693
|
`\u26A0 CEO hire is pending approval: ${agent._pendingApprovalId}. Approve it in the board before the bootstrap heartbeat can run.`
|
|
11513
11694
|
);
|
|
11514
|
-
if (agent._approvalAutoApproveError) {
|
|
11515
|
-
log(` Auto-approve failed: ${agent._approvalAutoApproveError}`);
|
|
11516
|
-
}
|
|
11517
11695
|
};
|
|
11518
11696
|
if (existingCompanyId) {
|
|
11519
11697
|
log("Looking for an existing CEO agent...");
|
|
@@ -11558,7 +11736,8 @@ var plugin = definePlugin({
|
|
|
11558
11736
|
adapterConfig,
|
|
11559
11737
|
instructionsBundle: ceoInstructionsBundle,
|
|
11560
11738
|
runtimeConfig: ceoRuntimeConfig,
|
|
11561
|
-
permissions: { canCreateAgents: true }
|
|
11739
|
+
permissions: { canCreateAgents: true },
|
|
11740
|
+
...boardOperationsIssue?.id ? { sourceIssueId: boardOperationsIssue.id } : {}
|
|
11562
11741
|
});
|
|
11563
11742
|
ceoAgentId = ceoAgent.id;
|
|
11564
11743
|
log(`\u2713 CEO agent created (${ceoAgentId})`);
|
|
@@ -11577,7 +11756,8 @@ var plugin = definePlugin({
|
|
|
11577
11756
|
adapterConfig,
|
|
11578
11757
|
instructionsBundle: ceoInstructionsBundle,
|
|
11579
11758
|
runtimeConfig: ceoRuntimeConfig,
|
|
11580
|
-
permissions: { canCreateAgents: true }
|
|
11759
|
+
permissions: { canCreateAgents: true },
|
|
11760
|
+
...boardOperationsIssue?.id ? { sourceIssueId: boardOperationsIssue.id } : {}
|
|
11581
11761
|
});
|
|
11582
11762
|
ceoAgentId = ceoAgent.id;
|
|
11583
11763
|
log(`\u2713 CEO agent created (${ceoAgentId})`);
|
|
@@ -11654,7 +11834,8 @@ var plugin = definePlugin({
|
|
|
11654
11834
|
adapterType,
|
|
11655
11835
|
adapterConfig: roleAdapterConfig,
|
|
11656
11836
|
instructionsBundle: roleInstructionsBundle,
|
|
11657
|
-
runtimeConfig: roleRuntimeConfig
|
|
11837
|
+
runtimeConfig: roleRuntimeConfig,
|
|
11838
|
+
...hiringPlanIssue?.id ? { sourceIssueId: hiringPlanIssue.id } : {}
|
|
11658
11839
|
});
|
|
11659
11840
|
teamAgentIds[roleName] = roleAgent.id;
|
|
11660
11841
|
log(`\u2713 ${roleTitle} created (${roleAgent.id})`);
|
|
@@ -11662,9 +11843,6 @@ var plugin = definePlugin({
|
|
|
11662
11843
|
log(
|
|
11663
11844
|
`\u26A0 ${roleTitle} hire pending approval: ${roleAgent._pendingApprovalId}. Approve it in the board.`
|
|
11664
11845
|
);
|
|
11665
|
-
if (roleAgent._approvalAutoApproveError) {
|
|
11666
|
-
log(` Auto-approve failed: ${roleAgent._approvalAutoApproveError}`);
|
|
11667
|
-
}
|
|
11668
11846
|
}
|
|
11669
11847
|
}
|
|
11670
11848
|
if (!existingCompanyId) {
|
|
@@ -11676,7 +11854,8 @@ var plugin = definePlugin({
|
|
|
11676
11854
|
const createdProject = await client.createProject(companyId, {
|
|
11677
11855
|
name: mainProject.name,
|
|
11678
11856
|
description: mainProject.description,
|
|
11679
|
-
workspace: mainProject.workspace
|
|
11857
|
+
workspace: mainProject.workspace,
|
|
11858
|
+
executionWorkspacePolicy: mainProject.executionWorkspacePolicy
|
|
11680
11859
|
});
|
|
11681
11860
|
mainProjectId = createdProject?.id;
|
|
11682
11861
|
log(
|
|
@@ -11753,14 +11932,17 @@ var plugin = definePlugin({
|
|
|
11753
11932
|
log("");
|
|
11754
11933
|
log("Provisioning complete!");
|
|
11755
11934
|
log(
|
|
11756
|
-
existingCompanyId ? "Bootstrap task created in existing company. Trigger the CEO heartbeat to continue setup." : "The CEO agent
|
|
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
|
|
11757
11939
|
);
|
|
11758
11940
|
return {
|
|
11759
11941
|
companyId,
|
|
11760
11942
|
issuePrefix: company.issuePrefix,
|
|
11761
11943
|
paperclipUrl,
|
|
11762
11944
|
agentIds: { ceo: ceoAgentId, ...teamAgentIds },
|
|
11763
|
-
issueIds
|
|
11945
|
+
issueIds,
|
|
11764
11946
|
logs
|
|
11765
11947
|
};
|
|
11766
11948
|
} catch (err) {
|