Repository navigation
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔒 PR Risk Scan ResultsScanned 12 changed file(s).
✅ No matching risk patterns were detected in changed files. Skipped non-text or missing files
|
🚦 Submission status: 👀 Ready for reviewRisk tier: Why this tier
Automated checks
Review
Commands
Updated for |
🧩 Canvas smoke test✅ Passed · 0 error(s), 8 warning(s)
Plugin
|
| Field | Value |
|---|---|
| Description | A local Copilot canvas for humans and agents to manage projects, features, and user stories. |
| Author | John Grant — https://github.com/johngrant |
| License | MIT |
| Keywords | kanban, project-management, user-stories, canvas |
| Extensions | kanvasban |
| Materialize | ✅ |
| Install | ✅ |
Changed plugin files
plugins/kanvasban/README.mdplugins/kanvasban/plugin.json
Extension kanvasban
| Field | Value |
|---|---|
| Registered by | kanvasban |
| Package | — |
| Preview | 1440×961, 81.9 KB |
| Modules | 5 (3 reachable from extension.mjs) |
| Files | 10 |
Permissions and capabilities (static analysis of modules reachable from extension.mjs)
- File system access (node:fs)
- Network sockets / local server (node:http, node:net, ...)
- Host information (node:os)
- Reads environment variables (process.env)
Warnings
- backlog.test.mjs: non-literal dynamic import cannot be analyzed safely (module is not reachable from extension.mjs)
- backlog.test.mjs: non-literal dynamic import cannot be analyzed safely (module is not reachable from extension.mjs)
- backlog.test.mjs: non-literal dynamic import cannot be analyzed safely (module is not reachable from extension.mjs)
- backlog.test.mjs: non-literal dynamic import cannot be analyzed safely (module is not reachable from extension.mjs)
- backlog.test.mjs: non-literal dynamic import cannot be analyzed safely (module is not reachable from extension.mjs)
- backlog.test.mjs: non-literal dynamic import cannot be analyzed safely (module is not reachable from extension.mjs)
- backlog.test.mjs: non-literal dynamic import cannot be analyzed safely (module is not reachable from extension.mjs)
- backlog.test.mjs: import "node:test" is not a Node.js builtin, host-provided package, or declared dependency (module is not reachable from extension.mjs)
Changed extension files
extensions/kanvasban/.gitignoreextensions/kanvasban/AGENTS.mdextensions/kanvasban/README.mdextensions/kanvasban/assets/preview.pngextensions/kanvasban/backlog-store.mjsextensions/kanvasban/backlog.test.mjsextensions/kanvasban/card-details.mjsextensions/kanvasban/extension.mjsextensions/kanvasban/index.htmlextensions/kanvasban/live-updates.mjs
Static checks compile modules without executing them. Minimum preview size is configurable with CANVAS_PREVIEW_MIN_WIDTH/CANVAS_PREVIEW_MIN_HEIGHT. Workflow run
There was a problem hiding this comment.
🟡 Changes recommended
Missing DNS-rebinding protection exposes personal board data, while test isolation and UI state defects also need correction.
10 open issues
Validate the Host header before serving requests · New Isolate USERPROFILE alongside HOME in the test fixture · New Include displayed field values in accessible names · New Restore inline-field focus after re-enabling its button · New Scope comment-save completion to its original draft · New Preserve focus when live updates rebuild comments · New Raise request limits to support valid field sizes · New Respect hidden state on archived Feature creation buttons · New Preserve an archived Feature as an open draft's parent · New Preserve keyboard focus across live board updates · New
What changed in this PR
Adds Kanvasban to the repository’s installable canvas plugins: a local, multi-project backlog shared by humans and agents without requiring GitHub issues or an external project-management service.
Changes:
- Adds a board for projects, features, and user stories, with browser controls and agent actions.
- Implements local persistence, first-run starter content, live updates, and automated tests.
- Registers the plugin and updates documentation and marketplace metadata.
| File | Description |
|---|---|
plugins/kanvasban/README.md |
Documents installation, features, and local storage. |
plugins/kanvasban/plugin.json |
Registers the plugin and reusable extension source. |
extensions/kanvasban/README.md |
Explains board behavior, actions, storage, and testing. |
extensions/kanvasban/live-updates.mjs |
Streams board snapshots and storage errors. |
extensions/kanvasban/index.html |
Implements the interactive board interface. |
extensions/kanvasban/extension.mjs |
Connects canvas lifecycle, HTTP routes, and agent actions. |
extensions/kanvasban/card-details.mjs |
Provides inline editing, comments, and history. |
extensions/kanvasban/backlog.test.mjs |
Adds storage, API, concurrency, and streaming coverage. |
extensions/kanvasban/backlog-store.mjs |
Implements validation, persistence, locking, and mutations. |
extensions/kanvasban/AGENTS.md |
Documents agent actions and coordination workflows. |
extensions/kanvasban/.gitignore |
Excludes local stores and temporary data. |
docs/README.plugins.md |
Adds Kanvasban to the plugin catalog. |
.github/plugin/marketplace.json |
Adds the marketplace installation entry. |
Review effort: Max
| async function handleHttpRequest(request, response) { | ||
| const url = new URL(request.url ?? '/', 'http://127.0.0.1') |
| const previousHome = process.env.HOME | ||
| const previousCopilotHome = process.env.COPILOT_HOME | ||
| t.after(() => { | ||
| if (previousHome === undefined) delete process.env.HOME | ||
| else process.env.HOME = previousHome | ||
| if (previousCopilotHome === undefined) delete process.env.COPILOT_HOME | ||
| else process.env.COPILOT_HOME = previousCopilotHome | ||
| }) | ||
| process.env.HOME = path.join(directory, 'home') |
| display.setAttribute('aria-label', `Edit ${label}`) | ||
| parent.append(root) | ||
| let editing = false | ||
| let saving = false | ||
| let baseline | ||
| const show = () => { | ||
| display.textContent = choices | ||
| ? choices().find(([value]) => value === (currentItem[field] ?? ''))?.[1] ?? 'Unavailable Feature' | ||
| : currentItem[field] || `Add ${label.toLowerCase()}...` | ||
| } |
| saving = false | ||
| accept.disabled = cancel.disabled = editor.disabled = false | ||
| find('#close-item').disabled = false | ||
| for (const other of fields.values()) other.setDisabled(false) |
| await save(editingId ? 'edit' : 'add', editingId, find('#comment-body').value) | ||
| resetEditor() |
| const comments = find('#comment-list') | ||
| comments.replaceChildren() |
| let size = 0 | ||
| for await (const chunk of request) { | ||
| size += chunk.length | ||
| if (size > 16384) throw new CanvasError('INVALID_INPUT', 'Request body exceeds the 16 KB limit.') |
| .archived-badge { display: inline-block; padding: 2px 6px; margin: 4px 0; border-radius: 3px; background: #dcdfe4; color: #44546f; font-size: .75rem; font-weight: 600; } | ||
| [data-theme="dark"] #toggle-archive[aria-pressed="true"] { background: #1c2b41; color: #85b8ff; } | ||
| [data-theme="dark"] .archived-badge { background: #454f59; color: #b6c2cf; } | ||
| .feature-create { display: grid; place-items: center; width: 32px; height: 32px; margin: .6rem 0 0 auto; padding: 0; font-size: 1.4rem; line-height: 1; } |
| option.textContent = title | ||
| parentFeature.append(option) | ||
| } | ||
| if (selectedParent && !features.some((feature) => feature.id === selectedParent)) { |
| (!story.archivedAt || toggleArchive.getAttribute('aria-pressed') === 'true') && (!priorityFilter.value || story.priority === priorityFilter.value) | ||
| && (!typeFilter.value || story.type === typeFilter.value) | ||
| && `${story.title} ${story.description} ${story.acceptanceCriteria} KB-${story.id} ${features.find((feature) => feature.id === story.parentFeatureId)?.title ?? ''}`.toLowerCase().includes(query)) | ||
| storyList.replaceChildren() |



Pull Request Checklist
npm startand verified thatREADME.mdis up to date.mainbranch for this pull request.Description
Add Kanvasban, a local Copilot canvas where humans and agents can manage projects, features, and linked or standalone user stories.
Unlike the existing accessibility and issue-triage boards, Kanvasban provides a personal, multi-project backlog without requiring GitHub issues or an external project-management service.
extensions/kanvasban/.plugins/kanvasban/, using the repository's source-composition namespace andassets/preview.pnglogo convention.Type of Contribution
Additional Notes
Validation
node --test extensions/kanvasban/backlog.test.mjs- passed, 30 tests.npm run plugin:validate- passed.npm run build- passed.npm run website:data- passed.npm start- passed; generated tracked files remained unchanged.git diff --cached --check- passed before committing.bash eng/fix-line-endings.sh- previously attempted; its GNU-stylesed -iinvocation is incompatible with macOSsed. Contribution files were separately checked for CRLF; none were found.Copilot host test
Loaded the contribution source as a user-level canvas extension in Copilot and opened it successfully. Verified that agent actions could read the existing projects. Then temporarily moved personal data aside and verified that first access created only the Hello world project, feature, and linked user story. A subsequent read returned the same project ID rather than reseeding. The author also manually exercised the canvas and captured screenshots.
Restored the original user extension and personal data after testing; original data and backup checksums matched before and after the test.
Data and optional support link
No personal board stores, backups, or runtime test data are included. The preview shows synthetic starter content. Contribution files were scanned for secrets and personal board names/IDs.
Board data is local to the user's Copilot home and shared across workspaces using that home; it is not cloud synchronization. The host supplies the canvas SDK.
The existing optional cup-and-heart tip button is retained and links to the author's Ko-fi page,
https://ko-fi.com/johnny888. Its icon embeds the original PNG within inline SVG, so rendering the canvas does not contact Ko-fi or load a remote widget script. Opening the support link is optional and is not required for any canvas feature. This author-affiliated link is disclosed for review under the paid-services guidance.By submitting this pull request, I confirm that my contribution abides by the Code of Conduct and will be licensed under the MIT License.