From fd59748a64d94f675ce69bdc6b9e1aeb3df3d378 Mon Sep 17 00:00:00 2001 From: "glm-5.1" Date: Tue, 28 Apr 2026 07:21:24 +0000 Subject: [PATCH] Set up project scaffold: Bun/OpenCode plugin with registry pattern - AGENTS.md adapted from taskgraph for this Bun/TypeScript plugin - Dual MIT/Apache-2.0 licenses - Package.json, tsconfig, biome config following open-memory pattern - Skeleton src/ with registry-pattern tasks tool (help operation working) - Opencode.json plugin config - .gitignore for dist, node_modules, lockb - Remove typo .gitingore files, proper .gitignore in .opencode/ --- .gitignore | 4 + .opencode/.gitingore | 4 - .opencode/agents/.gitingore | 4 - AGENTS.md | 180 ++++++++++++++++++++++++++++++++++ LICENSE | 10 ++ LICENSE-APACHE | 189 ++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 21 ++++ biome.json | 20 ++++ bun.lock | 121 +++++++++++++++++++++++ opencode.json | 4 + package.json | 52 ++++++++++ src/index.ts | 10 ++ src/tools.ts | 124 +++++++++++++++++++++++ tsconfig.json | 18 ++++ 14 files changed, 753 insertions(+), 8 deletions(-) create mode 100644 .gitignore delete mode 100644 .opencode/.gitingore delete mode 100644 .opencode/agents/.gitingore create mode 100644 AGENTS.md create mode 100644 LICENSE create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 biome.json create mode 100644 bun.lock create mode 100644 opencode.json create mode 100644 package.json create mode 100644 src/index.ts create mode 100644 src/tools.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f02deb --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +dist/ +node_modules/ +bun.lockb +bunfig.toml \ No newline at end of file diff --git a/.opencode/.gitingore b/.opencode/.gitingore deleted file mode 100644 index 25cb250..0000000 --- a/.opencode/.gitingore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -package.json -bun.lock -.gitignore \ No newline at end of file diff --git a/.opencode/agents/.gitingore b/.opencode/agents/.gitingore deleted file mode 100644 index 25cb250..0000000 --- a/.opencode/agents/.gitingore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -package.json -bun.lock -.gitignore \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a64150f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,180 @@ +# AGENTS.md + +## Project + +`@alkdev/open-tasks` — an OpenCode plugin that gives agents structured task management with graph analysis, decomposition guidance, and workflow cost estimation. Exposes a single `tasks` tool using a registry pattern (like open-memory and open-coordinator) to keep the agent's visible tool count minimal. + +Part of the alk.dev trio: +- **open-memory** (`memory` / `memory_compact`): session introspection, context awareness, history browsing +- **open-coordinator** (`worktree`): git worktree orchestration, session spawning, anomaly detection +- **open-tasks** (`tasks`): task graph management, dependency analysis, decomposition guidance + +## Repository + +- **Git**: `git@git.alk.dev:alkdev/open-tasks.git` +- **License**: MIT OR Apache-2.0 +- **Runtime**: Bun +- **Language**: TypeScript (strict, ESM, verbatimModuleSyntax) +- **Linter**: Biome (`bun run lint`, `bun run format`) +- **Build**: `bun run build` → `dist/` (bun build + tsc declarations) + +## Commands + +```bash +bun run build # bun build src/index.ts + tsc --emitDeclarationOnly +bun run typecheck # tsc --noEmit +bun run lint # biome check . +bun run format # biome format --write . +bun run test # bun test +``` + +**Always run** `bun run typecheck` and `bun run lint` after changes. + +## Architecture + +### Core Dependency: @alkdev/taskgraph + +The graph operations, risk scoring, frontmatter parsing, and analysis functions come from `@alkdev/taskgraph` — a pure TypeScript library built on graphology. This plugin wraps that library in an OpenCode tool interface. + +Key imports from `@alkdev/taskgraph`: +- `TaskGraph` — primary graph data structure (construction, queries, mutation, export) +- `parseTaskFile`, `parseTaskDirectory`, `parseFrontmatter`, `serializeFrontmatter` — YAML frontmatter I/O +- `criticalPath`, `weightedCriticalPath`, `parallelGroups`, `bottlenecks` — analysis functions +- `riskPath`, `riskDistribution`, `calculateTaskEv`, `workflowCost` — risk & cost analysis +- `shouldDecomposeTask` — decomposition guidance +- Categorical types: `TaskScope`, `TaskRisk`, `TaskImpact`, `TaskLevel`, `TaskPriority`, `TaskStatus` + +### Plugin Design: Registry Pattern + +Like open-memory, this plugin exposes **one tool** (`tasks`) with internal operation dispatch. This keeps the agent's visible tool count low. + +``` +tasks({tool: "help"}) → Show available operations +tasks({tool: "list"}) → List tasks in project +tasks({tool: "show", args: {id: "..."}}) → Show task details +tasks({tool: "deps", args: {id: "..."}}) → Show task dependencies +tasks({tool: "validate"}) → Validate all task files +... etc +``` + +### Source Structure + +``` +src/ +├── index.ts # Plugin entry: hooks + tool registration +├── tools.ts # Tool definitions (tasks router) +├── registry.ts # Operation registry pattern (dispatch by tool name) +├── operations/ # Individual operation implementations +│ ├── help.ts +│ ├── list.ts +│ ├── show.ts +│ ├── deps.ts +│ ├── validate.ts +│ └── ... (analysis operations) +└── formatting.ts # Output formatting helpers +``` + +### Plugin Hooks + +| Hook | Purpose | +|------|---------| +| None initial — future: task status injection into system prompt, worktree-aware task context | + +### The `tasks` Tool + +Single tool with `{tool, args}` dispatch. The `help` operation provides full reference with examples, following the pattern from open-memory's `memory({tool: "help"})`. + +Operations map to `@alkdev/taskgraph` functions, reading tasks from the project's `tasks/` directory and returning formatted output. + +## Local Development & Testing + +OpenCode installs plugins from npm into `~/.cache/opencode/node_modules/`. When doing local development, symlink your local repo: + +### Setup (one-time) + +```bash +rm -rf ~/.cache/opencode/node_modules/@alkdev/open-tasks +ln -s /workspace/@alkdev/open-tasks ~/.cache/opencode/node_modules/@alkdev/open-tasks +``` + +### Iteration loop + +```bash +bun run build # rebuild dist/index.js +bun run typecheck # verify types +bun run lint # verify style +bun run test # run tests +``` + +After rebuilding, restart OpenCode to pick up the new build. + +### Also clear Bun's global cache + +```bash +rm -rf ~/.bun/install/cache/@alkdev/open-tasks* +``` + +## Key Conventions + +- No comments unless requested +- ESM with `.js` extension in imports +- Strict TypeScript with `verbatimModuleSyntax` +- Biome for linting and formatting +- Task files are the source of truth (markdown with YAML frontmatter) +- Single tool with registry dispatch — minimize agent context bloat +- Include a `help` operation for discoverability + +## Relationship to Other Plugins + +- **open-memory** (`memory`, `memory_compact`): session history, context awareness — complementary +- **open-coordinator** (`worktree`): worktree orchestration — tasks drive what worktrees implement +- **taskgraph CLI** (`taskgraph`): Rust CLI for the same operations — this plugin is the TypeScript/OpenCode equivalent +- **@alkdev/taskgraph** (npm): Core library this plugin wraps — all graph operations come from here + +## Task File Format + +Tasks are markdown files in `tasks/` with YAML frontmatter: + +```yaml +--- +id: auth-setup +name: Setup Authentication +status: pending +depends_on: [] +scope: moderate +risk: medium +impact: component +level: implementation +--- + +## Description + +Implement OAuth2 authentication with provider abstraction. + +## Acceptance Criteria + +- [ ] OAuth2 flow works with Google provider +- [ ] Tokens stored securely + +## Notes + +> Agent fills this during implementation. + +## Summary + +> Agent fills this on completion. +``` + +## Build & Test Commands + +```bash +bun run build # bun build src/index.ts + tsc declarations +bun run typecheck # tsc --noEmit +bun run lint # biome check . +bun run format # biome format --write . +bun run test # bun test +``` + +## License + +Dual-licensed under MIT OR Apache-2.0. Both license files must be present at repository root. \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9851097 --- /dev/null +++ b/LICENSE @@ -0,0 +1,10 @@ +Dual licensed under: + +- MIT License () +- Apache License, Version 2.0 () + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. \ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..76ec812 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,189 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but not +limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if applicable. + The contents of the NOTICE file are for informational purposes + only and do not modify the License. You may add Your own + attribution notices within Derivative Works that You distribute, + alongside or as an addendum to the NOTICE text from the Work, + provided that such additional attribution notices cannot be + construed as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional license terms and conditions for use, +reproduction, or distribution of Your modifications, or for any such +Derivative Works as a whole, provided Your use, reproduction, and +distribution of the Work otherwise complies with the conditions +stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any Contributor, but only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, +or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +Copyright 2026 Alkimia Development + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..ede008e --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Alkimia Development + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..d0827e2 --- /dev/null +++ b/biome.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.13/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + } +} diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..46cc9d4 --- /dev/null +++ b/bun.lock @@ -0,0 +1,121 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "@alkdev/open-tasks", + "dependencies": { + "@alkdev/taskgraph": "^0.0.1", + "@opencode-ai/plugin": "^1.1.3", + }, + "devDependencies": { + "@types/bun": "^1.2.0", + "@types/node": "^20.14.0", + "typescript": "^5.7.3", + }, + }, + }, + "packages": { + "@alkdev/taskgraph": ["@alkdev/taskgraph@0.0.1", "", { "dependencies": { "@alkdev/typebox": "^0.34.49", "graphology": "^0.26.0", "graphology-components": "^1.5.4", "graphology-dag": "^0.4.1", "graphology-metrics": "^2.4.0", "graphology-operators": "^1.6.1", "yaml": "^2.8.3" } }, "sha512-U1EehRUXU1sTjHVgwOumU6EeIWsHHBoY5oxehs1iEZF05EO8uh+GaZfY6M5H0G2wzbSy7qw/exSq2DijWT0xwg=="], + + "@alkdev/typebox": ["@alkdev/typebox@0.34.49", "", {}, "sha512-hMidpI6GlMgQMlW9KEd8I3ywgewV6mva9iJaDuBfGtgeRAGrB8yyu6T/fHmgmyQineZ8l4/1PdH/VNr3S2er2g=="], + + "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], + + "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="], + + "@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="], + + "@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="], + + "@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="], + + "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="], + + "@opencode-ai/plugin": ["@opencode-ai/plugin@1.14.28", "", { "dependencies": { "@opencode-ai/sdk": "1.14.28", "effect": "4.0.0-beta.48", "zod": "4.1.8" }, "peerDependencies": { "@opentui/core": ">=0.1.105", "@opentui/solid": ">=0.1.105" }, "optionalPeers": ["@opentui/core", "@opentui/solid"] }, "sha512-cHJo7t1jwrzbkIVmNgggdWh4cyOVGw5fnbSpuYeL6qwfmH3g/6YLWtw5ZYEP6detUkEebT08mHXDGmsMUpQa+A=="], + + "@opencode-ai/sdk": ["@opencode-ai/sdk@1.14.28", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-qRFJfD+Zdz3jHHSupW4F6Io1ZFrQ6gCRFlG50O6kEU9xRxrBpK0wGvP+Y5VwwvD/gH9WKMHYinlQpDVI9/lgJQ=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], + + "@types/node": ["@types/node@20.19.39", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw=="], + + "@yomguithereal/helpers": ["@yomguithereal/helpers@1.1.1", "", {}, "sha512-UYvAq/XCA7xoh1juWDYsq3W0WywOB+pz8cgVnE1b45ZfdMhBvHDrgmSFG3jXeZSr2tMTYLGHFHON+ekG05Jebg=="], + + "bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "effect": ["effect@4.0.0-beta.48", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw=="], + + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], + + "fast-check": ["fast-check@4.7.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-NsZRtqvSSoCP0HbNjUD+r1JH8zqZalyp6gLY9e7OYs7NK9b6AHOs2baBFeBG7bVNsuoukh89x2Yg3rPsul8ziQ=="], + + "find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="], + + "graphology": ["graphology@0.26.0", "", { "dependencies": { "events": "^3.3.0" }, "peerDependencies": { "graphology-types": ">=0.24.0" } }, "sha512-8SSImzgUUYC89Z042s+0r/vMibY7GX/Emz4LDO5e7jYXhuoWfHISPFJYjpRLUSJGq6UQ6xlenvX1p/hJdfXuXg=="], + + "graphology-components": ["graphology-components@1.5.4", "", { "dependencies": { "graphology-indices": "^0.17.0", "graphology-utils": "^2.1.2" }, "peerDependencies": { "graphology-types": ">=0.19.0" } }, "sha512-O37vC226wgnN0C6FUWHNe4fbTzaF51CcQjwX3naId/QTzH/PkUtXaanCShj9ws5Vju+z4u3zvSeEZE84Bo9jlA=="], + + "graphology-dag": ["graphology-dag@0.4.1", "", { "dependencies": { "graphology-utils": "^2.4.1", "mnemonist": "^0.39.0" }, "peerDependencies": { "graphology-types": ">=0.19.0" } }, "sha512-3ch9oOAnHZDoT043vyg7ukmSkKJ505nFzaHaYOn0IF2PgGo5VtIavyVK4UpbIa4tli3hhGm1ZTdBsubTmaxu/w=="], + + "graphology-indices": ["graphology-indices@0.17.0", "", { "dependencies": { "graphology-utils": "^2.4.2", "mnemonist": "^0.39.0" }, "peerDependencies": { "graphology-types": ">=0.20.0" } }, "sha512-A7RXuKQvdqSWOpn7ZVQo4S33O0vCfPBnUSf7FwE0zNCasqwZVUaCXePuWo5HBpWw68KJcwObZDHpFk6HKH6MYQ=="], + + "graphology-metrics": ["graphology-metrics@2.4.0", "", { "dependencies": { "graphology-indices": "^0.17.0", "graphology-shortest-path": "^2.0.0", "graphology-utils": "^2.4.4", "mnemonist": "^0.39.0", "pandemonium": "2.4.1" }, "peerDependencies": { "graphology-types": ">=0.20.0" } }, "sha512-7WOfOP+mFLCaTJx55Qg4eY+211vr1/b3D/R3biz3SXGhAaCVcWYkfabnmO4O4WBNWANEHtVnFrGgJ0kj6MM6xw=="], + + "graphology-operators": ["graphology-operators@1.6.1", "", { "dependencies": { "graphology-utils": "^2.0.0" }, "peerDependencies": { "graphology-types": ">=0.20.0" } }, "sha512-ZKGcaN+6L5hv0VelrDgkZ2IQL1c7nrqkTRiHDwBCjmbkS56vWh/iQNDnvd/c9YIpoygtEK0mgGOr/m4i7BOYrw=="], + + "graphology-shortest-path": ["graphology-shortest-path@2.1.0", "", { "dependencies": { "@yomguithereal/helpers": "^1.1.1", "graphology-indices": "^0.17.0", "graphology-utils": "^2.4.3", "mnemonist": "^0.39.0" }, "peerDependencies": { "graphology-types": ">=0.20.0" } }, "sha512-KbT9CTkP/u72vGEJzyRr24xFC7usI9Es3LMmCPHGwQ1KTsoZjxwA9lMKxfU0syvT/w+7fZUdB/Hu2wWYcJBm6Q=="], + + "graphology-types": ["graphology-types@0.24.8", "", {}, "sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q=="], + + "graphology-utils": ["graphology-utils@2.5.2", "", { "peerDependencies": { "graphology-types": ">=0.23.0" } }, "sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ=="], + + "ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="], + + "mnemonist": ["mnemonist@0.39.8", "", { "dependencies": { "obliterator": "^2.0.1" } }, "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ=="], + + "msgpackr": ["msgpackr@1.11.10", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-iCZNq+HszvF+fC3anCm4nBmWEnbeIAfpDs6IStAEKhQ2YSgkjzVG2FF9XJqwwQh5bH3N9OUTUt4QwVN6MLMLtA=="], + + "msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="], + + "multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="], + + "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], + + "obliterator": ["obliterator@2.0.5", "", {}, "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw=="], + + "pandemonium": ["pandemonium@2.4.1", "", { "dependencies": { "mnemonist": "^0.39.2" } }, "sha512-wRqjisUyiUfXowgm7MFH2rwJzKIr20rca5FsHXCMNm1W5YPP1hCtrZfgmQ62kP7OZ7Xt+cR858aB28lu5NX55g=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="], + + "zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="], + } +} diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..bc32000 --- /dev/null +++ b/opencode.json @@ -0,0 +1,4 @@ +{ + "plugin": ["@alkdev/open-tasks"], + "$schema": "https://opencode.ai/config.json" +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8c9d501 --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "@alkdev/open-tasks", + "version": "0.1.0", + "description": "OpenCode plugin for structured task management with graph analysis, decomposition guidance, and workflow cost estimation.", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "scripts": { + "build": "bun build src/index.ts --outdir dist --target bun --format esm && tsc --emitDeclarationOnly", + "lint": "bunx @biomejs/biome check .", + "format": "bunx @biomejs/biome format --write .", + "typecheck": "tsc --noEmit", + "test": "bun test" + }, + "author": "Alkimia Development", + "license": "(MIT OR Apache-2.0)", + "repository": { + "type": "git", + "url": "git@git.alk.dev:alkdev/open-tasks.git" + }, + "files": [ + "dist", + "LICENSE-MIT", + "LICENSE-APACHE" + ], + "publishConfig": { + "access": "public" + }, + "keywords": [ + "opencode", + "plugin", + "tasks", + "taskgraph", + "dependency-analysis", + "decomposition" + ], + "dependencies": { + "@alkdev/taskgraph": "^0.0.1", + "@opencode-ai/plugin": "^1.1.3" + }, + "devDependencies": { + "@types/bun": "^1.2.0", + "@types/node": "^20.14.0", + "typescript": "^5.7.3" + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..e13d879 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,10 @@ +import type { Plugin } from "@opencode-ai/plugin"; +import { createTools } from "./tools.js"; + +const OpenTasksPlugin: Plugin = async (ctx) => { + return { + tool: createTools(ctx), + }; +}; + +export default OpenTasksPlugin; diff --git a/src/tools.ts b/src/tools.ts new file mode 100644 index 0000000..874e1da --- /dev/null +++ b/src/tools.ts @@ -0,0 +1,124 @@ +import type { PluginInput, ToolDefinition } from "@opencode-ai/plugin"; +import { tool } from "@opencode-ai/plugin"; + +const z = tool.schema; + +const HELP_TEXT = `# Tasks Tool + +Call \`tasks({tool: "", args: {...}})\` to use one. + +| Tool | Description | Key args | +|------|-------------|----------| +| list | List tasks in the project | status, scope | +| show | Show task details | id | +| deps | Show task dependencies (prerequisites) | id | +| dependents | Show tasks that depend on a task | id | +| validate | Validate all task files | — | +| critical | Show critical path | — | +| parallel | Show parallel execution groups | — | +| bottleneck | Show bottleneck analysis | — | +| risk | Show risk path and distribution | — | +| cost | Show workflow cost estimate | — | +| decompose | Check if a task should be decomposed | id | +| help | Show this reference, or details for a specific tool | tool | + +Examples: +- \`tasks({tool: "list"})\` +- \`tasks({tool: "show", args: {id: "auth-setup"}})\` +- \`tasks({tool: "deps", args: {id: "auth-setup"}})\` +- \`tasks({tool: "critical"})\` +- \`tasks({tool: "help", args: {tool: "show"}})\``; + +type ToolArgs = Record; + +type TaskHandler = (args: ToolArgs, ctx: PluginInput) => string | Promise; + +const handlers: Record = { + help(args) { + if (args.tool && typeof args.tool === "string") { + return `Details for "${args.tool}" — coming soon. Full implementation pending.`; + } + return HELP_TEXT; + }, + + list() { + return "Task listing — implementation pending. Tasks are read from the project's tasks/ directory."; + }, + + show(args) { + const id = (args.id as string) ?? "unknown"; + return `Task details for "${id}" — implementation pending.`; + }, + + deps(args) { + const id = (args.id as string) ?? "unknown"; + return `Dependencies for "${id}" — implementation pending.`; + }, + + dependents(args) { + const id = (args.id as string) ?? "unknown"; + return `Dependents of "${id}" — implementation pending.`; + }, + + validate() { + return "Task validation — implementation pending."; + }, + + critical() { + return "Critical path analysis — implementation pending."; + }, + + parallel() { + return "Parallel execution groups — implementation pending."; + }, + + bottleneck() { + return "Bottleneck analysis — implementation pending."; + }, + + risk() { + return "Risk path analysis — implementation pending."; + }, + + cost() { + return "Workflow cost estimate — implementation pending."; + }, + + decompose(args) { + const id = (args.id as string) ?? "unknown"; + return `Decomposition check for "${id}" — implementation pending.`; + }, +}; + +export function createTools(ctx: PluginInput): Record { + return { + tasks: tool({ + description: + 'Task graph management: list, show, analyze dependencies, critical path, risk, and workflow cost. Call tasks({tool: "help"}) for full reference.', + args: { + tool: z + .string() + .describe( + "Operation name: help, list, show, deps, dependents, validate, critical, parallel, bottleneck, risk, cost, decompose.", + ), + args: z + .record(z.string(), z.unknown()) + .optional() + .describe('Arguments for the operation. Call {tool: "help"} for details.'), + }, + async execute(input, _context) { + const toolName = input.tool; + const toolArgs = (input.args as ToolArgs) ?? {}; + const handler = handlers[toolName]; + if (!handler) { + return `Unknown operation: "${toolName}". Call tasks({tool: "help"}) for available operations.`; + } + try { + return await handler(toolArgs, ctx); + } catch (err) { + return `Error in ${toolName}: ${err instanceof Error ? err.message : String(err)}`; + } + }, + }), + }; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1ab1007 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "Bundler", + "types": ["node", "bun"], + "strict": true, + "noEmitOnError": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "dist", + "rootDir": "src", + "verbatimModuleSyntax": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"] +}