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