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

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.
@@ -0,0 +1,90 @@
1
+ # Contributing to Company Wizard
2
+
3
+ Thanks for your interest in contributing! Company Wizard is a [Paperclip](https://github.com/paperclipai/paperclip) plugin that bootstraps agent company workspaces from composable templates.
4
+
5
+ ## Getting Started
6
+
7
+ ```sh
8
+ git clone <repo-url>
9
+ cd paperclip-plugin-company-wizard
10
+ pnpm install
11
+ pnpm build
12
+ ```
13
+
14
+ ## Development
15
+
16
+ ```sh
17
+ pnpm build # esbuild: worker + manifest + UI → dist/
18
+ pnpm dev # watch mode (rebuilds on change)
19
+ pnpm test # vitest: tests/**/*.spec.ts
20
+ pnpm test:logic # node --test: src/logic/*.test.js
21
+ pnpm typecheck # tsc --noEmit
22
+ ```
23
+
24
+ Load the plugin in Paperclip by pointing to this directory (the `paperclipPlugin` field in `package.json` tells Paperclip where to find the built artifacts). After `pnpm build`, reload the plugin in the Paperclip UI — no reinstall required.
25
+
26
+ ## Project Structure
27
+
28
+ ```text
29
+ src/
30
+ ├── worker.ts # Plugin worker: actions (preview-files, start-provision, check-auth)
31
+ ├── manifest.ts # Plugin manifest (id, displayName, slots)
32
+ ├── logic/ # Pure functions (assembly, resolution, template loading)
33
+ ├── api/ # Paperclip REST API client and provisioning
34
+ └── ui/
35
+ ├── main.tsx # UI entry point
36
+ ├── context/ # WizardContext (state machine + reducer)
37
+ └── components/ # React components (WizardShell, step components, ConfigReview)
38
+
39
+ templates/
40
+ ├── roles/ # All roles with role.meta.json (base: true for always-present)
41
+ ├── modules/ # Composable capabilities with module.meta.json
42
+ └── presets/ # Curated module+role combinations with preset.meta.json
43
+ ```
44
+
45
+ ## Adding Templates
46
+
47
+ ### New module
48
+
49
+ See [docs/TEMPLATES.md](docs/TEMPLATES.md) for the full module schema. Key points:
50
+
51
+ - Every capability needs a shared skill in `skills/<skill>.md`
52
+ - Role-specific overrides go in `agents/<role>/skills/<skill>.md` (only when genuinely different)
53
+ - Fallback variants are always role-specific (`.fallback.md`)
54
+ - Add `node --test` tests in `src/logic/` if the module has non-trivial resolution logic
55
+
56
+ ### New role
57
+
58
+ See [docs/TEMPLATES.md](docs/TEMPLATES.md). Map `paperclipRole` to a valid Paperclip enum value. Set `base: true` only for roles that belong in every company (only the CEO currently has `base: true`).
59
+
60
+ ### New preset
61
+
62
+ See [docs/TEMPLATES.md](docs/TEMPLATES.md). Test that the module combination resolves correctly. Presets can include inline `goals[]` arrays with milestones and issues.
63
+
64
+ ## Pull Requests
65
+
66
+ - Keep PRs focused — one feature or fix per PR
67
+ - Add or update tests for logic changes
68
+ - Run `pnpm test && pnpm test:logic` before submitting
69
+ - Update `docs/TEMPLATES.md` if you add modules, roles, or presets
70
+ - Update `CHANGELOG.md` with your changes
71
+
72
+ ## Code Style
73
+
74
+ - TypeScript for plugin infrastructure (`src/worker.ts`, `src/manifest.ts`, `src/ui/`)
75
+ - ESM only (`type: "module"`)
76
+ - Plain JS for logic/API modules (`src/logic/`, `src/api/`) — JSDoc where helpful
77
+ - Prettier via pre-commit hook (lint-staged)
78
+
79
+ ## Reporting Issues
80
+
81
+ Use GitHub Issues. Include:
82
+
83
+ - What you expected vs what happened
84
+ - Plugin version (from `package.json`)
85
+ - Node.js version (`node --version`)
86
+ - OS
87
+
88
+ ## License
89
+
90
+ By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE).
package/README.md CHANGED
@@ -29,7 +29,7 @@
29
29
  #### Bootstrap reliability
30
30
 
31
31
  - **Agents provisioned with complete instructions** — every non-CEO agent is now created by the plugin directly with its full `instructionsBundle` (AGENTS.md + HEARTBEAT/SOUL/TOOLS + skills). Previously the CEO created these agents during bootstrap with only an `instructionsFilePath`, leaving each agent with a bare AGENTS.md and fragile external path references
32
- - **Routines created directly during provisioning** — Paperclip only allows an agent to create routines assigned to itself, so the CEO could not create routines owned by the Product Owner. The plugin now creates all routines with board authority at provisioning time; BOOTSTRAP.md tells the CEO they already exist
32
+ - **Routines created directly during provisioning** — Paperclip only allows an agent to create routines assigned to itself, so the CEO could not create routines owned by the Product Owner. The plugin now creates all routines with board authority at provisioning time and pre-creates the main project so every routine — including those owned by non-CEO agents — is linked to it; BOOTSTRAP.md tells the CEO they already exist
33
33
  - **Worker agents no longer run always-on heartbeats** — enabling heartbeats on every provisioned agent caused bursts of concurrent runs that crashed the dev server. Only the CEO keeps an always-on heartbeat; all other agents are woken on assignment
34
34
  - **Fresh local repos no longer bootstrap with isolated git worktrees** — provisioning a brand-new `local_path` project with an `isolated_workspace` / `git_worktree` policy made worker agents try to branch off `main` before the repo existed, so early runs failed and agents flipped to `error`. The isolated policy is now suppressed for fresh local repos (agents work in the shared project workspace during bootstrap); existing external repos keep it. Guarded in `assemble.js` and removed at the source in `StepRepository` and the AI wizard prompts
35
35
  - **Workspace isolation follows Paperclip instance settings** — `enableIsolatedWorktrees` is no longer a plugin setting. The wizard reads `enableIsolatedWorkspaces` from the Paperclip instance experimental settings and only applies `isolated_workspace` / `git_worktree` for external repositories when that setting is enabled.
@@ -42,6 +42,7 @@
42
42
  - **Doc references use relative paths** (`docs/<file>`) instead of absolute paths that baked in the collision-suffixed company directory name
43
43
  - Duplicate bootstrap issues (same title from module + preset) are now deduplicated; preset issue wins
44
44
  - CFO role removed — was orphaned (no preset, no capability, never activated)
45
+ - **PR bodies and comments use `--body-file`** — review skills posted GitHub PR bodies and comments with inline `gh pr … --body "..."`, where a double-quoted shell string keeps `\n` literal, so multi-line Markdown rendered as literal `text\ntext`. All PR-review guidance now writes Markdown to a file and uses `--body-file`, with a verdict-heading comment template documented in `pr-conventions.md`
45
46
 
46
47
  #### AI wizard
47
48
 
package/dist/manifest.js CHANGED
@@ -2,7 +2,7 @@
2
2
  var manifest = {
3
3
  id: "starlein.paperclip-plugin-company-wizard",
4
4
  apiVersion: 1,
5
- version: "0.3.21",
5
+ version: "0.3.23",
6
6
  displayName: "Company Wizard",
7
7
  description: "AI-powered wizard to bootstrap agent companies from composable templates",
8
8
  author: "Sascha Pietrowski <sp@speednetwork.de>",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/manifest.ts"],
4
- "sourcesContent": ["import type { PaperclipPluginManifestV1 } from '@paperclipai/plugin-sdk';\n\nconst manifest: PaperclipPluginManifestV1 = {\n id: 'starlein.paperclip-plugin-company-wizard',\n apiVersion: 1,\n version: '0.3.21',\n displayName: 'Company Wizard',\n description: 'AI-powered wizard to bootstrap agent companies from composable templates',\n author: 'Sascha Pietrowski <sp@speednetwork.de>',\n categories: ['workspace', 'ui'],\n capabilities: [\n 'companies.read',\n 'issues.create',\n 'issues.read',\n 'issues.update',\n 'goals.create',\n 'goals.read',\n 'agents.read',\n 'projects.read',\n 'plugin.state.read',\n 'plugin.state.write',\n 'secrets.read-ref',\n 'events.subscribe',\n 'ui.page.register',\n 'ui.sidebar.register',\n ],\n instanceConfigSchema: {\n type: 'object',\n properties: {\n companiesDir: {\n type: 'string',\n description:\n 'Directory where assembled company workspaces are written. Defaults to ~/.paperclip/instances/default/companies. Override for Docker setups (e.g. /paperclip/instances/default/companies).',\n },\n templatesPath: {\n type: 'string',\n description:\n 'Path to the templates directory. Defaults to ~/.paperclip/plugin-templates (auto-downloaded from templatesRepoUrl if missing). Override for Docker setups (e.g. /paperclip/plugin-templates).',\n },\n templatesRepoUrl: {\n type: 'string',\n default: 'https://github.com/starlein/paperclip-plugin-company-wizard/tree/main/templates',\n description:\n 'GitHub tree URL to pull templates from when the templates directory does not exist.',\n },\n anthropicApiKey: {\n type: 'string',\n description:\n 'Anthropic API key for the AI wizard (e.g. sk-ant-...). Required to use the AI-powered company setup path.',\n },\n paperclipUrl: {\n type: 'string',\n description:\n 'Paperclip instance URL. Defaults to http://localhost:3100 or the PAPERCLIP_PUBLIC_URL env var.',\n },\n paperclipEmail: {\n type: 'string',\n description: 'Board login email (for authenticated instances).',\n },\n paperclipPassword: {\n type: 'string',\n description: 'Board login password (for authenticated instances).',\n },\n disableBoardApprovalOnNewCompanies: {\n type: 'boolean',\n default: false,\n description:\n 'Optional. If true, the wizard will PATCH new companies to set requireBoardApprovalForNewAgents=false during provisioning. Leave false to preserve approval-gated hiring policies.',\n },\n enableEnrichedPersonas: {\n type: 'boolean',\n default: false,\n description:\n 'Optional. If true, expert roles are enriched with domain lenses (named mental models), module skills gain concrete output/review bars with negative examples, and HEARTBEAT.md gains explicit done-criteria. Leave false (default) for the lean baseline personas.',\n },\n },\n },\n entrypoints: {\n worker: './dist/worker.js',\n ui: './dist/ui',\n },\n ui: {\n slots: [\n {\n type: 'page',\n id: 'company-wizard',\n displayName: 'Company Wizard',\n exportName: 'WizardPage',\n routePath: 'company-creator',\n },\n {\n type: 'sidebar',\n id: 'company-wizard-link',\n displayName: 'Create Company',\n exportName: 'SidebarLink',\n },\n ],\n },\n};\n\nexport default manifest;\n"],
4
+ "sourcesContent": ["import type { PaperclipPluginManifestV1 } from '@paperclipai/plugin-sdk';\n\nconst manifest: PaperclipPluginManifestV1 = {\n id: 'starlein.paperclip-plugin-company-wizard',\n apiVersion: 1,\n version: '0.3.23',\n displayName: 'Company Wizard',\n description: 'AI-powered wizard to bootstrap agent companies from composable templates',\n author: 'Sascha Pietrowski <sp@speednetwork.de>',\n categories: ['workspace', 'ui'],\n capabilities: [\n 'companies.read',\n 'issues.create',\n 'issues.read',\n 'issues.update',\n 'goals.create',\n 'goals.read',\n 'agents.read',\n 'projects.read',\n 'plugin.state.read',\n 'plugin.state.write',\n 'secrets.read-ref',\n 'events.subscribe',\n 'ui.page.register',\n 'ui.sidebar.register',\n ],\n instanceConfigSchema: {\n type: 'object',\n properties: {\n companiesDir: {\n type: 'string',\n description:\n 'Directory where assembled company workspaces are written. Defaults to ~/.paperclip/instances/default/companies. Override for Docker setups (e.g. /paperclip/instances/default/companies).',\n },\n templatesPath: {\n type: 'string',\n description:\n 'Path to the templates directory. Defaults to ~/.paperclip/plugin-templates (auto-downloaded from templatesRepoUrl if missing). Override for Docker setups (e.g. /paperclip/plugin-templates).',\n },\n templatesRepoUrl: {\n type: 'string',\n default: 'https://github.com/starlein/paperclip-plugin-company-wizard/tree/main/templates',\n description:\n 'GitHub tree URL to pull templates from when the templates directory does not exist.',\n },\n anthropicApiKey: {\n type: 'string',\n description:\n 'Anthropic API key for the AI wizard (e.g. sk-ant-...). Required to use the AI-powered company setup path.',\n },\n paperclipUrl: {\n type: 'string',\n description:\n 'Paperclip instance URL. Defaults to http://localhost:3100 or the PAPERCLIP_PUBLIC_URL env var.',\n },\n paperclipEmail: {\n type: 'string',\n description: 'Board login email (for authenticated instances).',\n },\n paperclipPassword: {\n type: 'string',\n description: 'Board login password (for authenticated instances).',\n },\n disableBoardApprovalOnNewCompanies: {\n type: 'boolean',\n default: false,\n description:\n 'Optional. If true, the wizard will PATCH new companies to set requireBoardApprovalForNewAgents=false during provisioning. Leave false to preserve approval-gated hiring policies.',\n },\n enableEnrichedPersonas: {\n type: 'boolean',\n default: false,\n description:\n 'Optional. If true, expert roles are enriched with domain lenses (named mental models), module skills gain concrete output/review bars with negative examples, and HEARTBEAT.md gains explicit done-criteria. Leave false (default) for the lean baseline personas.',\n },\n },\n },\n entrypoints: {\n worker: './dist/worker.js',\n ui: './dist/ui',\n },\n ui: {\n slots: [\n {\n type: 'page',\n id: 'company-wizard',\n displayName: 'Company Wizard',\n exportName: 'WizardPage',\n routePath: 'company-creator',\n },\n {\n type: 'sidebar',\n id: 'company-wizard-link',\n displayName: 'Create Company',\n exportName: 'SidebarLink',\n },\n ],\n },\n};\n\nexport default manifest;\n"],
5
5
  "mappings": ";AAEA,IAAM,WAAsC;AAAA,EAC1C,IAAI;AAAA,EACJ,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,aAAa;AAAA,EACb,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,YAAY,CAAC,aAAa,IAAI;AAAA,EAC9B,cAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,sBAAsB;AAAA,IACpB,MAAM;AAAA,IACN,YAAY;AAAA,MACV,cAAc;AAAA,QACZ,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,eAAe;AAAA,QACb,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,kBAAkB;AAAA,QAChB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,aACE;AAAA,MACJ;AAAA,MACA,iBAAiB;AAAA,QACf,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,cAAc;AAAA,QACZ,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,gBAAgB;AAAA,QACd,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,mBAAmB;AAAA,QACjB,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,oCAAoC;AAAA,QAClC,MAAM;AAAA,QACN,SAAS;AAAA,QACT,aACE;AAAA,MACJ;AAAA,MACA,wBAAwB;AAAA,QACtB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,aACE;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX,QAAQ;AAAA,IACR,IAAI;AAAA,EACN;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,aAAa;AAAA,QACb,YAAY;AAAA,QACZ,WAAW;AAAA,MACb;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,aAAa;AAAA,QACb,YAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,mBAAQ;",
6
6
  "names": []
7
7
  }
package/dist/worker.js CHANGED
@@ -10116,10 +10116,21 @@ Read: \`docs/${doc}\`
10116
10116
  };
10117
10117
  const mainProject = resolvedProjects[0];
10118
10118
  const mainProjectName = mainProject?.name || companyName;
10119
+ const mainProjectInfo = mainProject ? {
10120
+ name: mainProjectName,
10121
+ description: mainProject.description || "",
10122
+ workspace: normalizeProjectWorkspace(mainProject)
10123
+ } : null;
10124
+ const mainProjectPreCreated = initialRoutines.length > 0 && mainProjectInfo !== null;
10119
10125
  if (resolvedProjects.length > 0) {
10120
10126
  bootstrap += `## Projects
10121
10127
 
10122
10128
  `;
10129
+ 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.
10131
+
10132
+ `;
10133
+ }
10123
10134
  for (const proj of resolvedProjects) {
10124
10135
  const workspace = normalizeProjectWorkspace(proj);
10125
10136
  bootstrap += `### ${proj.name}
@@ -10312,14 +10323,20 @@ Read: \`docs/${doc}\`
10312
10323
  bootstrap += `${stepN++}. **Create goal** "${g.title}" (level: ${level}${parentNote})
10313
10324
  `;
10314
10325
  }
10315
- for (const proj of resolvedProjects) {
10326
+ resolvedProjects.forEach((proj, idx) => {
10316
10327
  const workspace = normalizeProjectWorkspace(proj);
10317
10328
  const goalLinks = proj.goals?.length > 0 ? `, goalIds \u2192 [${proj.goals.map((g) => `"${g}"`).join(", ")}]` : "";
10318
10329
  const activePolicy = effectiveExecutionPolicy(proj, workspace);
10319
10330
  const policy = activePolicy?.defaultMode ? `, executionWorkspacePolicy.defaultMode: "${activePolicy.defaultMode}"` : "";
10320
- bootstrap += `${stepN++}. **Create project** "${proj.name}" (workspace: ${formatWorkspaceObject(workspace)}${policy}${goalLinks})
10331
+ if (idx === 0 && mainProjectPreCreated) {
10332
+ const goalLinkInstruction = goalLinks ? ` After creating the goals above, link them to it (${goalLinks.replace(/^, /, "")}).` : "";
10333
+ 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}
10321
10334
  `;
10322
- }
10335
+ } else {
10336
+ bootstrap += `${stepN++}. **Create project** "${proj.name}" (workspace: ${formatWorkspaceObject(workspace)}${policy}${goalLinks})
10337
+ `;
10338
+ }
10339
+ });
10323
10340
  if (bootstrapLabels.length > 0) {
10324
10341
  bootstrap += `${stepN++}. **Create labels** \u2014 create each label exactly as listed in the Labels section before creating issues
10325
10342
  `;
@@ -10338,7 +10355,14 @@ Read: \`docs/${doc}\`
10338
10355
  `;
10339
10356
  await writeFile(join(companyDir, "BOOTSTRAP.md"), bootstrap);
10340
10357
  onProgress("+ BOOTSTRAP.md");
10341
- return { companyDir, allRoles, initialIssues, initialRoutines, roleAdapterOverrides };
10358
+ return {
10359
+ companyDir,
10360
+ allRoles,
10361
+ initialIssues,
10362
+ initialRoutines,
10363
+ roleAdapterOverrides,
10364
+ mainProject: mainProjectInfo
10365
+ };
10342
10366
  }
10343
10367
 
10344
10368
  // src/api/client.js
@@ -11599,6 +11623,25 @@ var plugin = definePlugin({
11599
11623
  }
11600
11624
  if (!existingCompanyId) {
11601
11625
  const routines = Array.isArray(assembleResult.initialRoutines) ? assembleResult.initialRoutines : [];
11626
+ let mainProjectId;
11627
+ const mainProject = assembleResult.mainProject;
11628
+ if (routines.length > 0 && mainProject?.name) {
11629
+ try {
11630
+ const createdProject = await client.createProject(companyId, {
11631
+ name: mainProject.name,
11632
+ description: mainProject.description,
11633
+ workspace: mainProject.workspace
11634
+ });
11635
+ mainProjectId = createdProject?.id;
11636
+ log(
11637
+ `\u2713 Main project "${mainProject.name}" created${mainProjectId ? ` (${mainProjectId})` : " (no id returned)"}`
11638
+ );
11639
+ } catch (err) {
11640
+ log(
11641
+ `\u26A0 Could not create main project "${mainProject.name}": ${err instanceof Error ? err.message : String(err)}. Routines will be created without a project.`
11642
+ );
11643
+ }
11644
+ }
11602
11645
  for (const routine of routines) {
11603
11646
  const title = typeof routine.title === "string" && routine.title.trim() ? routine.title.trim() : typeof routine.name === "string" ? routine.name.trim() : "";
11604
11647
  if (!title) continue;
@@ -11609,6 +11652,7 @@ var plugin = definePlugin({
11609
11652
  title,
11610
11653
  description: routine.description,
11611
11654
  assigneeAgentId,
11655
+ projectId: mainProjectId,
11612
11656
  priority: routine.priority || "medium",
11613
11657
  concurrencyPolicy: routine.concurrencyPolicy || "skip_if_active"
11614
11658
  });