opencode-leetcode-realworld 0.2.3 → 0.4.0
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/README.md +58 -2
- package/package.json +1 -1
- package/src/index.ts +24 -1
- package/src/languages.ts +473 -119
- package/src/patterns.ts +2 -1
- package/src/prompt.ts +73 -49
- package/src/scaffold.ts +157 -4
- package/src/schema.ts +226 -0
- package/src/types.ts +22 -1
package/README.md
CHANGED
|
@@ -76,6 +76,7 @@ Options can be given in any order; the command figures out which is which.
|
|
|
76
76
|
| `tags` | One or more topic tags, comma-separated (e.g. `graph` or `dynamic-programming,sliding-window`) | any | Nudges which underlying algorithm and real-world domain you get. |
|
|
77
77
|
| `<id-or-slug>` | A problem number or slug (e.g. `1`, `two-sum`) | random | Derive the assignment from one specific source problem. |
|
|
78
78
|
| `language` | A supported language or alias (see below) | **asks you** | Language of the generated project and tests. |
|
|
79
|
+
| `scope` | `single` \| `mini` \| `full` | **asks you** | How big the assignment is (see Two modes). |
|
|
79
80
|
|
|
80
81
|
If you leave the language (or difficulty) out, opencode **asks you** with a
|
|
81
82
|
multiple-choice question before generating anything.
|
|
@@ -150,17 +151,34 @@ they tend to produce:
|
|
|
150
151
|
```
|
|
151
152
|
/practice
|
|
152
153
|
│
|
|
153
|
-
├─ (agent)
|
|
154
|
+
├─ (agent) ask you for language/difficulty, then how you want to start
|
|
154
155
|
│
|
|
155
156
|
├─ leetcode_fetch fetch a problem from the LeetCode API (private reasoning input)
|
|
156
157
|
│
|
|
157
158
|
├─ (agent) derive the principle -> invent a real-world scenario
|
|
158
159
|
│
|
|
159
|
-
├─ leetcode_scaffold write
|
|
160
|
+
├─ leetcode_scaffold write the exercises(s): one function, or a multi-file app
|
|
160
161
|
│
|
|
161
162
|
└─ guard hooks block reading hidden tests + provenance, keep the exercise honest
|
|
162
163
|
```
|
|
163
164
|
|
|
165
|
+
## Two modes
|
|
166
|
+
|
|
167
|
+
`/practice` asks **how you want to start**, and the answer changes what gets built:
|
|
168
|
+
|
|
169
|
+
| Scope | What you get |
|
|
170
|
+
| --- | --- |
|
|
171
|
+
| **Quick exercise** | One entry point with a JSON-in / JSON-out contract (any language). |
|
|
172
|
+
| **Mini project** | A small multi-file app (~3-6 files) with the algorithm behind a single `TODO` (TypeScript, Python, Rust). |
|
|
173
|
+
| **Fuller project** | A multi-file app with more realistic layers (~6-15 files) and the same single `TODO`. |
|
|
174
|
+
|
|
175
|
+
In project mode you get a real little codebase — CLI entry, domain types, service
|
|
176
|
+
layer, wiring — where **everything works except one `TODO`**. You implement that
|
|
177
|
+
piece (the algorithm) and `node tests/runner.mjs` verifies the app end-to-end.
|
|
178
|
+
|
|
179
|
+
Project mode is available for **TypeScript, Python, Rust**; the other languages use
|
|
180
|
+
the quick exercise.
|
|
181
|
+
|
|
164
182
|
The generated project looks like this:
|
|
165
183
|
|
|
166
184
|
```
|
|
@@ -182,6 +200,43 @@ node tests/runner.mjs # all cases
|
|
|
182
200
|
node tests/runner.mjs --public # visible cases only
|
|
183
201
|
```
|
|
184
202
|
|
|
203
|
+
## Typed starters
|
|
204
|
+
|
|
205
|
+
The starter file is generated from your test cases, so the input/output contract
|
|
206
|
+
is explicit before you write any logic:
|
|
207
|
+
|
|
208
|
+
- **Type-safe languages** (`typescript`, `go`, `rust`, `csharp`, `swift`) get real
|
|
209
|
+
type declarations inferred from the sample inputs/outputs — an interface /
|
|
210
|
+
struct / class per object, with nested objects and arrays typed too — plus a
|
|
211
|
+
typed function signature. Your editor autocompletes `payload.<field>`.
|
|
212
|
+
- **Dynamic languages** (`javascript`, `python`, `ruby`, `php`) — and Swift when a
|
|
213
|
+
shape can't be expressed statically — get a commented input/output format with a
|
|
214
|
+
concrete example instead.
|
|
215
|
+
|
|
216
|
+
Example (TypeScript), generated from input `{ "values": [1,2,3] }` -> output `[3,2,1]`:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
interface Payload {
|
|
220
|
+
values: number[];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function solve(payload: Payload): number[] {
|
|
224
|
+
throw new Error("Not implemented: solve");
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Example (Python):
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
# Input format: { values: number[] }
|
|
232
|
+
# Input example: {"values":[1,2,3]}
|
|
233
|
+
# Output format: number[]
|
|
234
|
+
# Output example: [3,2,1]
|
|
235
|
+
|
|
236
|
+
def solve(payload):
|
|
237
|
+
raise NotImplementedError("Not implemented: solve")
|
|
238
|
+
```
|
|
239
|
+
|
|
185
240
|
## Running from source
|
|
186
241
|
|
|
187
242
|
For plugin development, clone the repo and open opencode inside it — files under
|
|
@@ -234,6 +289,7 @@ src/
|
|
|
234
289
|
index.ts plugin entry: tools, guard hooks, command registration
|
|
235
290
|
leetcode.ts API client + normalizer (handles endpoint shape differences)
|
|
236
291
|
languages.ts per-language adapters: starter, build, run commands
|
|
292
|
+
schema.ts input/output schema inference + type generation
|
|
237
293
|
patterns.ts topic -> domain seeds, leak detection
|
|
238
294
|
scaffold.ts deterministic project writer (any supported language)
|
|
239
295
|
guard.ts protected-path detection
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-leetcode-realworld",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "OpenCode plugin that turns a LeetCode problem into a realistic software-engineering assignment, so you practice applying the underlying algorithm instead of solving the puzzle.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/index.ts
CHANGED
|
@@ -71,9 +71,15 @@ export const LeetCodeRealWorld: Plugin = async ({ client, directory }) => {
|
|
|
71
71
|
|
|
72
72
|
const scaffoldTool = tool({
|
|
73
73
|
description:
|
|
74
|
-
"Create a real-world practice project from a spec
|
|
74
|
+
"Create a real-world practice project from a spec. " +
|
|
75
|
+
"Use mode \"single\" for one JSON-in/out function (any language) or mode \"project\" " +
|
|
76
|
+
"(typescript, python, rust) for a multi-file app where one TODO must be implemented. " +
|
|
75
77
|
"Rejects any spec that leaks the original problem, its title, slug, or a coding-practice site name.",
|
|
76
78
|
args: {
|
|
79
|
+
mode: tool.schema
|
|
80
|
+
.enum(["single", "project"])
|
|
81
|
+
.default("single")
|
|
82
|
+
.describe("single = one function; project = multi-file app (ts/python/rust)"),
|
|
77
83
|
title: tool.schema.string().describe("Real-world project title (no coding-practice words)"),
|
|
78
84
|
scenario: tool.schema.string().describe("Business context, written like a real ticket"),
|
|
79
85
|
pattern: tool.schema.string().describe("Underlying algorithmic principle (internal only)"),
|
|
@@ -102,6 +108,22 @@ export const LeetCodeRealWorld: Plugin = async ({ client, directory }) => {
|
|
|
102
108
|
outDir: tool.schema.string().optional().describe("Target directory, relative to the session directory"),
|
|
103
109
|
sourceSlug: tool.schema.string().optional().describe("Provenance only; never surfaced"),
|
|
104
110
|
sourceTitle: tool.schema.string().optional().describe("Provenance only; never surfaced"),
|
|
111
|
+
task: tool.schema
|
|
112
|
+
.string()
|
|
113
|
+
.optional()
|
|
114
|
+
.describe("Project mode: markdown brief telling the learner what to implement and where"),
|
|
115
|
+
files: tool.schema
|
|
116
|
+
.array(tool.schema.object({ path: tool.schema.string(), content: tool.schema.string() }))
|
|
117
|
+
.optional()
|
|
118
|
+
.describe("Project mode: the full file tree, leaving one TODO to implement"),
|
|
119
|
+
runCommand: tool.schema
|
|
120
|
+
.string()
|
|
121
|
+
.optional()
|
|
122
|
+
.describe("Project mode: command that runs one case (JSON on stdin, JSON on stdout)"),
|
|
123
|
+
buildCommand: tool.schema
|
|
124
|
+
.string()
|
|
125
|
+
.optional()
|
|
126
|
+
.describe("Project mode: optional one-time build command (compiled languages)"),
|
|
105
127
|
},
|
|
106
128
|
async execute(rawArgs, context) {
|
|
107
129
|
const spec = rawArgs as ScaffoldSpec;
|
|
@@ -131,6 +153,7 @@ export const LeetCodeRealWorld: Plugin = async ({ client, directory }) => {
|
|
|
131
153
|
`Created a real-world practice project at ${relative}`,
|
|
132
154
|
"",
|
|
133
155
|
`Title: ${result.title}`,
|
|
156
|
+
`Mode: ${result.mode}${result.mode === "project" ? " (implement the marked TODO)" : ""}`,
|
|
134
157
|
`Language: ${result.languageName} (${result.runtime})`,
|
|
135
158
|
`Files (${result.files.length}):`,
|
|
136
159
|
...result.files.map((file) => ` - ${file}`),
|