@dezkareid/osddt 1.10.1 → 1.11.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/AGENTS.md +60 -8
- package/README.md +59 -19
- package/dist/commands/setup-worktree.d.ts +2 -0
- package/dist/commands/start-worktree.d.ts +9 -0
- package/dist/commands/worktree-info.d.ts +2 -0
- package/dist/index.js +394 -11
- package/package.json +3 -4
- package/dist/commands/context.spec.d.ts +0 -1
- package/dist/commands/done.spec.d.ts +0 -1
- package/dist/commands/meta-info.spec.d.ts +0 -1
- package/dist/commands/setup.spec.d.ts +0 -1
- package/dist/commands/update.spec.d.ts +0 -1
- package/dist/templates/claude.spec.d.ts +0 -1
- package/dist/templates/gemini.spec.d.ts +0 -1
- package/dist/templates/shared.spec.d.ts +0 -1
package/AGENTS.md
CHANGED
|
@@ -82,7 +82,6 @@ Always prefer use exact versions for dependencies. Do not use `^` or `~`.
|
|
|
82
82
|
|
|
83
83
|
#### Linting & Formatting
|
|
84
84
|
- **ESLint**: `9.39.2`
|
|
85
|
-
- **Prettier**: `3.8.1`
|
|
86
85
|
|
|
87
86
|
#### Type Definitions
|
|
88
87
|
- **@types/node**: `25.0.10`
|
|
@@ -155,10 +154,20 @@ The selected agents are saved in `.osddtrc` alongside `repoType`. When `osddt up
|
|
|
155
154
|
| `npx @dezkareid/osddt setup --agents <list> --repo-type <type>` | External | Non-interactive setup (for CI/scripted environments) |
|
|
156
155
|
| `osddt meta-info` | Local dev | Output current branch and date as JSON |
|
|
157
156
|
| `npx @dezkareid/osddt meta-info` | External | Output current branch and date as JSON |
|
|
158
|
-
| `osddt done <feature-name> --dir <project-path>`
|
|
159
|
-
| `npx @dezkareid/osddt done <feature-name> --dir <project-path>`
|
|
160
|
-
| `osddt
|
|
161
|
-
| `npx @dezkareid/osddt
|
|
157
|
+
| `osddt done <feature-name> --dir <project-path>` | Local dev | Move `working-on/<feature>` to `done/<feature>` |
|
|
158
|
+
| `npx @dezkareid/osddt done <feature-name> --dir <project-path>` | External | Move `working-on/<feature>` to `done/<feature>` |
|
|
159
|
+
| `osddt done <feature-name> --dir <project-path> --worktree` | Local dev | Archive feature, remove git worktree, and clean state file |
|
|
160
|
+
| `npx @dezkareid/osddt done <feature-name> --dir <project-path> --worktree` | External | Archive feature, remove git worktree, and clean state file |
|
|
161
|
+
| `osddt update` | Local dev | Regenerate agent command files from the existing `.osddtrc` |
|
|
162
|
+
| `npx @dezkareid/osddt update` | External | Regenerate agent command files from the existing `.osddtrc` |
|
|
163
|
+
| `osddt start-worktree <feature-name>` | Local dev | Create a git worktree for a feature and scaffold working-on/ |
|
|
164
|
+
| `npx @dezkareid/osddt start-worktree <feature-name>` | External | Create a git worktree for a feature and scaffold working-on/ |
|
|
165
|
+
| `osddt start-worktree <feature-name> --dir <package-path>` | Local dev | Same, specifying the package path in a monorepo |
|
|
166
|
+
| `npx @dezkareid/osddt start-worktree <feature-name> --dir <package-path>` | External | Same, specifying the package path in a monorepo |
|
|
167
|
+
| `osddt worktree-info <feature-name>` | Local dev | Look up worktree paths for a feature (JSON output) |
|
|
168
|
+
| `npx @dezkareid/osddt worktree-info <feature-name>` | External | Look up worktree paths for a feature (JSON output) |
|
|
169
|
+
| `osddt setup-worktree` | Local dev | Validate environment for git worktree usage |
|
|
170
|
+
| `npx @dezkareid/osddt setup-worktree` | External | Validate environment for git worktree usage |
|
|
162
171
|
|
|
163
172
|
#### `osddt setup` options
|
|
164
173
|
|
|
@@ -178,8 +187,9 @@ Templates are generated by `npx @dezkareid/osddt setup` and placed in each agent
|
|
|
178
187
|
| ------------------ | ------------------------------------------------------------------ |
|
|
179
188
|
| `osddt.continue` | Detect the current workflow phase and prompt the next command |
|
|
180
189
|
| `osddt.research` | Research a topic and write a research file to inform the spec |
|
|
181
|
-
| `osddt.start`
|
|
182
|
-
| `osddt.
|
|
190
|
+
| `osddt.start` | Start a new feature by creating a branch and working-on folder |
|
|
191
|
+
| `osddt.start-worktree` | Start a new feature using a git worktree for parallel development |
|
|
192
|
+
| `osddt.spec` | Analyze requirements and write a feature specification |
|
|
183
193
|
| `osddt.clarify` | Resolve open questions in the spec and record decisions (optional) |
|
|
184
194
|
| `osddt.plan` | Create a technical implementation plan from a specification |
|
|
185
195
|
| `osddt.tasks` | Generate actionable tasks from an implementation plan |
|
|
@@ -249,6 +259,35 @@ Templates are generated by `npx @dezkareid/osddt setup` and placed in each agent
|
|
|
249
259
|
/osddt.done
|
|
250
260
|
```
|
|
251
261
|
|
|
262
|
+
##### Example D — Parallel feature workflow (git worktree)
|
|
263
|
+
|
|
264
|
+
Use `osddt.start-worktree` to work on multiple features simultaneously. Each feature gets its own isolated directory (a git worktree), so you can keep multiple editor windows open without switching branches.
|
|
265
|
+
|
|
266
|
+
Before using worktrees for the first time, validate your environment:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
npx @dezkareid/osddt setup-worktree
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Then start a feature in its own worktree:
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
/osddt.start-worktree add-payment-gateway
|
|
276
|
+
/osddt.spec
|
|
277
|
+
/osddt.plan use Stripe SDK, REST endpoints
|
|
278
|
+
/osddt.tasks
|
|
279
|
+
/osddt.implement
|
|
280
|
+
/osddt.done
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
The worktree is created as a sibling of the repo root (e.g. `../my-repo-add-payment-gateway`). Active worktrees are tracked in `.osddt-worktrees` (sibling to the repo root) — a JSON array with entries containing `featureName`, `branch`, `worktreePath`, `workingDir`, and `repoRoot`. `osddt.continue` and `osddt.done` read this file to resolve the correct paths. When `osddt.done` runs, it archives the planning folder **and** removes the worktree automatically.
|
|
284
|
+
|
|
285
|
+
You can customise the worktree base directory via `worktreeBase` in `.osddtrc`:
|
|
286
|
+
|
|
287
|
+
```json
|
|
288
|
+
{ "repoType": "single", "worktreeBase": "/Users/me/worktrees" }
|
|
289
|
+
```
|
|
290
|
+
|
|
252
291
|
---
|
|
253
292
|
|
|
254
293
|
- Use **`osddt.research`** when you want to explore the codebase and gather findings before writing the spec. It creates the `working-on/<feature-name>/` folder and writes `osddt.research.md`.
|
|
@@ -304,6 +343,17 @@ Templates are generated by `npx @dezkareid/osddt setup` and placed in each agent
|
|
|
304
343
|
4. Records all new answers in a `## Decisions` section of `osddt.spec.md` and removes each answered question from the **Open Questions** section (removing the section heading too if it becomes empty).
|
|
305
344
|
5. Always prompts the user to run (or re-run) `osddt.plan` to reflect the updated decisions.
|
|
306
345
|
|
|
346
|
+
#### osddt.start-worktree behaviour
|
|
347
|
+
|
|
348
|
+
- **Input**: Either a human-readable feature description or an existing branch name.
|
|
349
|
+
- **Branch name resolution**: same rules as `osddt.start`.
|
|
350
|
+
- **Actions performed by the agent**:
|
|
351
|
+
1. Derives the branch and feature name.
|
|
352
|
+
2. Runs `npx @dezkareid/osddt start-worktree <feature-name> [--dir <package-path>]`.
|
|
353
|
+
3. The CLI creates the git worktree as a sibling of the repo root (`../<repo-name>-<feature-name>`, overridable via `worktreeBase` in `.osddtrc`), scaffolds `working-on/<feature-name>/` inside the worktree, and writes an entry to `.osddt-worktrees` (sibling to repo root).
|
|
354
|
+
4. Reports the branch, worktree path, and working directory.
|
|
355
|
+
5. Next step: `/osddt.spec`.
|
|
356
|
+
|
|
307
357
|
#### osddt.done behaviour
|
|
308
358
|
|
|
309
359
|
- **Input**: None — the feature is identified automatically.
|
|
@@ -311,7 +361,9 @@ Templates are generated by `npx @dezkareid/osddt setup` and placed in each agent
|
|
|
311
361
|
1. Reads `.osddtrc` to resolve the project path (single vs monorepo). For monorepos, asks the user which package.
|
|
312
362
|
2. Lists all folders under `working-on/`. If there is only one, uses it automatically; if there are multiple, asks the user to pick one.
|
|
313
363
|
3. Confirms all tasks in `osddt.tasks.md` are checked off (`- [x]`).
|
|
314
|
-
4. Runs `npx @dezkareid/osddt
|
|
364
|
+
4. Runs `npx @dezkareid/osddt worktree-info <feature-name>` to check if this is a worktree feature.
|
|
365
|
+
- If found: runs `npx @dezkareid/osddt done <feature-name> --dir <project-path> --worktree` (archives folder, removes worktree, cleans state file).
|
|
366
|
+
- If not found: runs `npx @dezkareid/osddt done <feature-name> --dir <project-path>` (existing behaviour).
|
|
315
367
|
|
|
316
368
|
Note: the `osddt done` CLI command does **not** read `.osddtrc` — project path resolution is the agent's responsibility, handled in step 1 above.
|
|
317
369
|
|
package/README.md
CHANGED
|
@@ -3,13 +3,18 @@ Other spec driven development tool but for monorepo
|
|
|
3
3
|
|
|
4
4
|
## CLI Commands
|
|
5
5
|
|
|
6
|
-
| Command
|
|
7
|
-
|
|
|
8
|
-
| `@dezkareid/osddt setup`
|
|
9
|
-
| `@dezkareid/osddt setup --agents <list> --repo-type <type>`
|
|
10
|
-
| `@dezkareid/osddt meta-info`
|
|
11
|
-
| `@dezkareid/osddt done <feature-name> --dir <project-path>`
|
|
12
|
-
| `@dezkareid/osddt
|
|
6
|
+
| Command | Description |
|
|
7
|
+
| -------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
8
|
+
| `@dezkareid/osddt setup` | Generate agent command files for Claude and Gemini |
|
|
9
|
+
| `@dezkareid/osddt setup --agents <list> --repo-type <type>` | Non-interactive setup (for CI/scripted environments) |
|
|
10
|
+
| `@dezkareid/osddt meta-info` | Output current branch and date as JSON |
|
|
11
|
+
| `@dezkareid/osddt done <feature-name> --dir <project-path>` | Move `working-on/<feature>` to `done/<feature>` |
|
|
12
|
+
| `@dezkareid/osddt done <feature-name> --dir <project-path> --worktree` | Archive feature, remove git worktree, and clean state file |
|
|
13
|
+
| `@dezkareid/osddt update` | Regenerate agent command files from the existing `.osddtrc` |
|
|
14
|
+
| `@dezkareid/osddt start-worktree <feature-name>` | Create a git worktree for a feature and scaffold working-on/ |
|
|
15
|
+
| `@dezkareid/osddt start-worktree <feature-name> --dir <package-path>` | Same, specifying the package path in a monorepo |
|
|
16
|
+
| `@dezkareid/osddt worktree-info <feature-name>` | Look up worktree paths for a feature (JSON output) |
|
|
17
|
+
| `@dezkareid/osddt setup-worktree` | Validate environment for git worktree usage |
|
|
13
18
|
|
|
14
19
|
### `osddt setup` options
|
|
15
20
|
|
|
@@ -106,6 +111,40 @@ flowchart LR
|
|
|
106
111
|
|
|
107
112
|
> Review the `## Assumptions` section of `osddt.plan.md` before implementing. Run `/osddt.clarify` if any Open Questions in the spec need resolving first.
|
|
108
113
|
|
|
114
|
+
#### Parallel feature workflow (git worktree)
|
|
115
|
+
|
|
116
|
+
Use `osddt.start-worktree` when you want to work on multiple features simultaneously. Each feature gets its own isolated directory so you can keep multiple editor windows open without switching branches.
|
|
117
|
+
|
|
118
|
+
Before using worktrees for the first time, validate your environment:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npx @dezkareid/osddt setup-worktree
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Then start a feature in its own worktree:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
/osddt.start-worktree add-payment-gateway
|
|
128
|
+
/osddt.spec
|
|
129
|
+
/osddt.plan use Stripe SDK, REST endpoints
|
|
130
|
+
/osddt.tasks
|
|
131
|
+
/osddt.implement
|
|
132
|
+
/osddt.done
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The worktree is created as a sibling of your repo (e.g. `../my-repo-add-payment-gateway`). The `working-on/` planning folder lives inside the worktree alongside the code. When `osddt.done` runs, it archives the planning folder **and** removes the worktree automatically.
|
|
136
|
+
|
|
137
|
+
You can customise the worktree base directory by adding `worktreeBase` to `.osddtrc`:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"repoType": "single",
|
|
142
|
+
"worktreeBase": "/Users/me/worktrees"
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Active worktrees are tracked in `.osddt-worktrees` (sibling to the repo root), a JSON array with entries containing `featureName`, `branch`, `worktreePath`, `workingDir`, and `repoRoot`. This file is the single source of truth used by `osddt.continue` and `osddt.done` to resolve the correct paths.
|
|
147
|
+
|
|
109
148
|
#### Resuming after closing a session
|
|
110
149
|
|
|
111
150
|
```
|
|
@@ -118,18 +157,19 @@ flowchart LR
|
|
|
118
157
|
/osddt.done
|
|
119
158
|
```
|
|
120
159
|
|
|
121
|
-
| Template
|
|
122
|
-
|
|
|
123
|
-
| `osddt.continue`
|
|
124
|
-
| `osddt.research`
|
|
125
|
-
| `osddt.start`
|
|
126
|
-
| `osddt.
|
|
127
|
-
| `osddt.
|
|
128
|
-
| `osddt.
|
|
129
|
-
| `osddt.
|
|
130
|
-
| `osddt.
|
|
131
|
-
| `osddt.
|
|
132
|
-
| `osddt.
|
|
160
|
+
| Template | Description |
|
|
161
|
+
| ----------------------- | ------------------------------------------------------------------ |
|
|
162
|
+
| `osddt.continue` | Detect the current workflow phase and prompt the next command |
|
|
163
|
+
| `osddt.research` | Research a topic and write a research file to inform the spec |
|
|
164
|
+
| `osddt.start` | Start a new feature by creating a branch and working-on folder |
|
|
165
|
+
| `osddt.start-worktree` | Start a new feature using a git worktree for parallel development |
|
|
166
|
+
| `osddt.spec` | Analyze requirements and write a feature specification |
|
|
167
|
+
| `osddt.clarify` | Resolve open questions in the spec and record decisions (optional) |
|
|
168
|
+
| `osddt.plan` | Create a technical implementation plan from a specification |
|
|
169
|
+
| `osddt.tasks` | Generate actionable tasks from an implementation plan |
|
|
170
|
+
| `osddt.implement` | Execute tasks from the task list one by one |
|
|
171
|
+
| `osddt.fast` | Bootstrap all planning artifacts (spec, plan, tasks) in one shot |
|
|
172
|
+
| `osddt.done` | Resolve project path, verify tasks, and move the feature to done |
|
|
133
173
|
|
|
134
174
|
Generated files are placed in:
|
|
135
175
|
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
import select from '@inquirer/select';
|
|
7
7
|
import checkbox from '@inquirer/checkbox';
|
|
8
8
|
import { execSync } from 'child_process';
|
|
9
|
+
import readline from 'readline';
|
|
9
10
|
|
|
10
11
|
function getRepoPreamble(npxCommand) {
|
|
11
12
|
return `## Context
|
|
@@ -121,6 +122,15 @@ const COMMAND_DEFINITIONS = [
|
|
|
121
122
|
|
|
122
123
|
## Instructions
|
|
123
124
|
|
|
125
|
+
Before checking the working directory, run the following command to check whether this feature uses a git worktree:
|
|
126
|
+
|
|
127
|
+
\`\`\`
|
|
128
|
+
${npxCommand} worktree-info <feature-name>
|
|
129
|
+
\`\`\`
|
|
130
|
+
|
|
131
|
+
- If it exits with code **0**: parse the JSON output and use the returned \`workingDir\` as \`<project-path>/working-on/<feature-name>\`. Skip the main-tree scan below.
|
|
132
|
+
- If it exits with code **1**: the feature is not a worktree feature. Use the standard project path from \`.osddtrc\` and scan the main tree as usual.
|
|
133
|
+
|
|
124
134
|
Check the working directory \`<project-path>/working-on/<feature-name>\` for the files listed below **in order** to determine the current phase. Use the first matching condition:
|
|
125
135
|
|
|
126
136
|
| Condition | Current phase | Run next |
|
|
@@ -222,6 +232,45 @@ ${getCustomContextStep(npxCommand, 'start')}## Arguments
|
|
|
222
232
|
|
|
223
233
|
${args}
|
|
224
234
|
|
|
235
|
+
${getNextStepToSpec(args)}
|
|
236
|
+
`,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: 'osddt.start-worktree',
|
|
240
|
+
description: 'Start a new feature using a git worktree for parallel development',
|
|
241
|
+
body: ({ args, npxCommand }) => `${getRepoPreamble(npxCommand)}## Instructions
|
|
242
|
+
|
|
243
|
+
The argument provided is: ${args}
|
|
244
|
+
|
|
245
|
+
Determine the branch name using the following logic:
|
|
246
|
+
|
|
247
|
+
1. If ${args} looks like a branch name (e.g. \`feat/my-feature\`, \`fix/some-bug\`, \`my-feature-branch\` — no spaces, kebab-case or slash-separated), use it as-is.
|
|
248
|
+
2. Otherwise treat ${args} as a human-readable feature description, convert it to a feature name, and use the format \`feat/<derived-name>\` as the branch name.
|
|
249
|
+
|
|
250
|
+
Apply the constraints below to the feature name (the segment after the last \`/\`) before using it:
|
|
251
|
+
|
|
252
|
+
${FEATURE_NAME_RULES}
|
|
253
|
+
|
|
254
|
+
Once the branch name is determined:
|
|
255
|
+
|
|
256
|
+
3. Run the following command to create the git worktree, scaffold the working directory, and register the feature in the state file:
|
|
257
|
+
|
|
258
|
+
\`\`\`
|
|
259
|
+
${npxCommand} start-worktree <feature-name>
|
|
260
|
+
\`\`\`
|
|
261
|
+
|
|
262
|
+
For monorepos, pass the package path:
|
|
263
|
+
|
|
264
|
+
\`\`\`
|
|
265
|
+
${npxCommand} start-worktree <feature-name> --dir <package-path>
|
|
266
|
+
\`\`\`
|
|
267
|
+
|
|
268
|
+
4. Report the branch name, worktree path, and working directory shown in the command output.
|
|
269
|
+
|
|
270
|
+
${getCustomContextStep(npxCommand, 'start')}## Arguments
|
|
271
|
+
|
|
272
|
+
${args}
|
|
273
|
+
|
|
225
274
|
${getNextStepToSpec(args)}
|
|
226
275
|
`,
|
|
227
276
|
},
|
|
@@ -502,16 +551,53 @@ ${args}
|
|
|
502
551
|
body: ({ npxCommand }) => `## Instructions
|
|
503
552
|
|
|
504
553
|
1. Confirm all tasks in \`osddt.tasks.md\` are checked off (\`- [x]\`)
|
|
505
|
-
2. Run the following command to
|
|
554
|
+
2. Run the following command to check whether this feature uses a git worktree:
|
|
506
555
|
|
|
507
556
|
\`\`\`
|
|
508
|
-
${npxCommand}
|
|
557
|
+
${npxCommand} worktree-info <feature-name>
|
|
509
558
|
\`\`\`
|
|
510
559
|
|
|
511
|
-
|
|
560
|
+
3. Based on the result:
|
|
561
|
+
|
|
562
|
+
**If it exits with code 1 (standard feature):** use the project path from \`.osddtrc\`, then run:
|
|
563
|
+
\`\`\`
|
|
564
|
+
${npxCommand} done <feature-name> --dir <project-path>
|
|
565
|
+
\`\`\`
|
|
566
|
+
Skip to step 8.
|
|
567
|
+
|
|
568
|
+
**If it exits with code 0 (worktree feature):** parse the JSON to get \`worktreePath\` and \`branch\`, derive \`<project-path>\` from \`workingDir\`, then continue below.
|
|
569
|
+
|
|
570
|
+
4. Check for uncommitted changes inside the worktree:
|
|
571
|
+
|
|
572
|
+
\`\`\`
|
|
573
|
+
git -C <worktreePath> status --porcelain
|
|
574
|
+
\`\`\`
|
|
575
|
+
|
|
576
|
+
5. If there are **uncommitted changes**:
|
|
577
|
+
1. Run \`git -C <worktreePath> diff\` to inspect them.
|
|
578
|
+
2. Derive a concise commit message in **conventional commit** format (e.g. \`feat: add payment gateway integration\`) based on the diff.
|
|
579
|
+
3. Present the proposed message to the user: _"Use this commit message, or provide your own?"_
|
|
580
|
+
4. Once confirmed, commit:
|
|
581
|
+
\`\`\`
|
|
582
|
+
git -C <worktreePath> add -A
|
|
583
|
+
git -C <worktreePath> commit -m "<confirmed-message>"
|
|
584
|
+
\`\`\`
|
|
585
|
+
|
|
586
|
+
6. Push the branch to remote (covers both first push and subsequent pushes):
|
|
587
|
+
|
|
588
|
+
\`\`\`
|
|
589
|
+
git -C <worktreePath> push --set-upstream origin <branch>
|
|
590
|
+
\`\`\`
|
|
591
|
+
|
|
592
|
+
7. Run the done command with the \`--worktree\` flag:
|
|
593
|
+
\`\`\`
|
|
594
|
+
${npxCommand} done <feature-name> --dir <project-path> --worktree
|
|
595
|
+
\`\`\`
|
|
596
|
+
|
|
597
|
+
8. The command will automatically prefix the destination folder name with today's date in \`YYYY-MM-DD\` format.
|
|
512
598
|
For example, \`working-on/feature-a\` will be moved to \`done/YYYY-MM-DD-feature-a\`.
|
|
513
599
|
|
|
514
|
-
|
|
600
|
+
9. Report the result of the command, including the full destination path
|
|
515
601
|
|
|
516
602
|
${getCustomContextStep(npxCommand, 'done')}`,
|
|
517
603
|
},
|
|
@@ -527,7 +613,7 @@ ${body}`;
|
|
|
527
613
|
}
|
|
528
614
|
function getClaudeTemplates(cwd, npxCommand) {
|
|
529
615
|
const dir = path.join(cwd, CLAUDE_COMMANDS_DIR);
|
|
530
|
-
return COMMAND_DEFINITIONS.map(
|
|
616
|
+
return COMMAND_DEFINITIONS.map(cmd => ({
|
|
531
617
|
filePath: path.join(dir, `${cmd.name}.md`),
|
|
532
618
|
content: formatClaudeCommand(cmd.description, cmd.body({ args: '$ARGUMENTS', npxCommand })),
|
|
533
619
|
}));
|
|
@@ -543,7 +629,7 @@ ${body}"""
|
|
|
543
629
|
}
|
|
544
630
|
function getGeminiTemplates(cwd, npxCommand) {
|
|
545
631
|
const dir = path.join(cwd, GEMINI_COMMANDS_DIR);
|
|
546
|
-
return COMMAND_DEFINITIONS.map(
|
|
632
|
+
return COMMAND_DEFINITIONS.map(cmd => ({
|
|
547
633
|
filePath: path.join(dir, `${cmd.name}.toml`),
|
|
548
634
|
content: formatGeminiCommand(cmd.description, cmd.body({ args: '{{args}}', npxCommand })),
|
|
549
635
|
}));
|
|
@@ -608,12 +694,12 @@ async function resolveNpxCommand(cwd) {
|
|
|
608
694
|
const VALID_AGENTS = ['claude', 'gemini'];
|
|
609
695
|
const VALID_REPO_TYPES = ['single', 'monorepo'];
|
|
610
696
|
function parseAgents(raw) {
|
|
611
|
-
const values = raw.split(',').map(
|
|
697
|
+
const values = raw.split(',').map(s => s.trim());
|
|
612
698
|
if (values.length === 0) {
|
|
613
699
|
console.error('Error: --agents requires at least one value.');
|
|
614
700
|
process.exit(1);
|
|
615
701
|
}
|
|
616
|
-
const invalid = values.filter(
|
|
702
|
+
const invalid = values.filter(v => !VALID_AGENTS.includes(v));
|
|
617
703
|
if (invalid.length > 0) {
|
|
618
704
|
console.error(`Error: Invalid agent(s): ${invalid.join(', ')}. Valid values: ${VALID_AGENTS.join(', ')}.`);
|
|
619
705
|
process.exit(1);
|
|
@@ -712,7 +798,13 @@ function todayPrefix() {
|
|
|
712
798
|
const dd = String(now.getDate()).padStart(2, '0');
|
|
713
799
|
return `${yyyy}-${mm}-${dd}`;
|
|
714
800
|
}
|
|
715
|
-
|
|
801
|
+
function resolveRepoRoot$2(cwd) {
|
|
802
|
+
return execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim();
|
|
803
|
+
}
|
|
804
|
+
function stateFilePath$2(repoRoot) {
|
|
805
|
+
return path.join(path.dirname(repoRoot), '.osddt-worktrees');
|
|
806
|
+
}
|
|
807
|
+
async function runDone(featureName, cwd, worktree) {
|
|
716
808
|
const src = path.join(cwd, 'working-on', featureName);
|
|
717
809
|
const destName = `${todayPrefix()}-${featureName}`;
|
|
718
810
|
const dest = path.join(cwd, 'done', destName);
|
|
@@ -723,6 +815,30 @@ async function runDone(featureName, cwd) {
|
|
|
723
815
|
await fs.ensureDir(path.dirname(dest));
|
|
724
816
|
await fs.move(src, dest);
|
|
725
817
|
console.log(`Moved: working-on/${featureName} → done/${destName}`);
|
|
818
|
+
if (!worktree)
|
|
819
|
+
return;
|
|
820
|
+
const repoRoot = resolveRepoRoot$2(process.cwd());
|
|
821
|
+
const stateFile = stateFilePath$2(repoRoot);
|
|
822
|
+
if (!(await fs.pathExists(stateFile))) {
|
|
823
|
+
console.error(`Warning: .osddt-worktrees not found at ${stateFile}. Skipping worktree cleanup.`);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
const entries = await fs.readJson(stateFile);
|
|
827
|
+
const entry = entries.find(e => e.featureName === featureName);
|
|
828
|
+
if (!entry) {
|
|
829
|
+
console.error(`Warning: No worktree entry found for "${featureName}". Skipping worktree cleanup.`);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
if (await fs.pathExists(entry.worktreePath)) {
|
|
833
|
+
execSync(`git worktree remove "${entry.worktreePath}" --force`, { cwd: repoRoot, stdio: 'inherit' });
|
|
834
|
+
console.log(`Removed worktree: ${entry.worktreePath}`);
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
console.log(`Worktree path not found on filesystem, skipping git worktree remove.`);
|
|
838
|
+
}
|
|
839
|
+
const updated = entries.filter(e => e.featureName !== featureName);
|
|
840
|
+
await fs.writeJson(stateFile, updated, { spaces: 2 });
|
|
841
|
+
console.log(`Removed state entry for "${featureName}" from .osddt-worktrees`);
|
|
726
842
|
}
|
|
727
843
|
function doneCommand() {
|
|
728
844
|
const cmd = new Command('done');
|
|
@@ -730,9 +846,10 @@ function doneCommand() {
|
|
|
730
846
|
.description('Move a feature from working-on/<feature-name> to done/<feature-name>')
|
|
731
847
|
.argument('<feature-name>', 'name of the feature to mark as done')
|
|
732
848
|
.option('-d, --dir <directory>', 'project directory', process.cwd())
|
|
849
|
+
.option('--worktree', 'also remove the git worktree and clean up the state file')
|
|
733
850
|
.action(async (featureName, options) => {
|
|
734
851
|
const targetDir = path.resolve(options.dir);
|
|
735
|
-
await runDone(featureName, targetDir);
|
|
852
|
+
await runDone(featureName, targetDir, options.worktree ?? false);
|
|
736
853
|
});
|
|
737
854
|
return cmd;
|
|
738
855
|
}
|
|
@@ -745,7 +862,7 @@ async function hasOsddtCommandFile(dir, pattern) {
|
|
|
745
862
|
if (!(await fs.pathExists(dir)))
|
|
746
863
|
return false;
|
|
747
864
|
const entries = await fs.readdir(dir);
|
|
748
|
-
return entries.some(
|
|
865
|
+
return entries.some(f => pattern.test(f));
|
|
749
866
|
}
|
|
750
867
|
async function inferAgents(cwd) {
|
|
751
868
|
const detected = [];
|
|
@@ -868,6 +985,269 @@ function contextCommand() {
|
|
|
868
985
|
return cmd;
|
|
869
986
|
}
|
|
870
987
|
|
|
988
|
+
function resolveRepoRoot$1(cwd) {
|
|
989
|
+
return execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim();
|
|
990
|
+
}
|
|
991
|
+
function repoName(repoRoot) {
|
|
992
|
+
return path.basename(repoRoot);
|
|
993
|
+
}
|
|
994
|
+
function stateFilePath$1(repoRoot) {
|
|
995
|
+
return path.join(path.dirname(repoRoot), '.osddt-worktrees');
|
|
996
|
+
}
|
|
997
|
+
async function readStateFile(stateFile) {
|
|
998
|
+
if (!(await fs.pathExists(stateFile)))
|
|
999
|
+
return [];
|
|
1000
|
+
return fs.readJson(stateFile);
|
|
1001
|
+
}
|
|
1002
|
+
async function writeStateFile(stateFile, entries) {
|
|
1003
|
+
await fs.writeJson(stateFile, entries, { spaces: 2 });
|
|
1004
|
+
}
|
|
1005
|
+
function branchExists(branch, cwd) {
|
|
1006
|
+
try {
|
|
1007
|
+
execSync(`git rev-parse --verify ${branch}`, { cwd, stdio: 'ignore' });
|
|
1008
|
+
return true;
|
|
1009
|
+
}
|
|
1010
|
+
catch {
|
|
1011
|
+
return false;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
function remoteBranchExists(branch, cwd) {
|
|
1015
|
+
try {
|
|
1016
|
+
execSync(`git ls-remote --exit-code --heads origin ${branch}`, { cwd, stdio: 'ignore' });
|
|
1017
|
+
return true;
|
|
1018
|
+
}
|
|
1019
|
+
catch {
|
|
1020
|
+
return false;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
async function prompt(question) {
|
|
1024
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1025
|
+
return new Promise((resolve) => {
|
|
1026
|
+
rl.question(question, (answer) => {
|
|
1027
|
+
rl.close();
|
|
1028
|
+
resolve(answer.trim());
|
|
1029
|
+
});
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
async function createWorktree(branch, worktreePath, repoRoot) {
|
|
1033
|
+
if (await fs.pathExists(worktreePath)) {
|
|
1034
|
+
console.log(`\nDirectory already exists at: ${worktreePath}`);
|
|
1035
|
+
const answer = await prompt('Resume or Abort? [R/a] ');
|
|
1036
|
+
if (answer.toLowerCase() === 'a') {
|
|
1037
|
+
console.log('Aborted.');
|
|
1038
|
+
process.exit(0);
|
|
1039
|
+
}
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
const localExists = branchExists(branch, repoRoot);
|
|
1043
|
+
const remoteExists = !localExists && remoteBranchExists(branch, repoRoot);
|
|
1044
|
+
if (localExists || remoteExists) {
|
|
1045
|
+
console.log(`\nBranch "${branch}" already exists ${localExists ? 'locally' : 'on remote'}.`);
|
|
1046
|
+
const answer = await prompt('Resume or Abort? [R/a] ');
|
|
1047
|
+
if (answer.toLowerCase() === 'a') {
|
|
1048
|
+
console.log('Aborted.');
|
|
1049
|
+
process.exit(0);
|
|
1050
|
+
}
|
|
1051
|
+
execSync(`git worktree add "${worktreePath}" ${branch}`, { cwd: repoRoot, stdio: 'inherit' });
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
execSync(`git worktree add "${worktreePath}" -b ${branch}`, { cwd: repoRoot, stdio: 'inherit' });
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
async function runStartWorktree(featureName, options) {
|
|
1058
|
+
const cwd = process.cwd();
|
|
1059
|
+
const repoRoot = resolveRepoRoot$1(cwd);
|
|
1060
|
+
const branch = `feat/${featureName}`;
|
|
1061
|
+
// Read .osddtrc
|
|
1062
|
+
const rcPath = path.join(repoRoot, '.osddtrc');
|
|
1063
|
+
let rc = { repoType: 'single' };
|
|
1064
|
+
if (await fs.pathExists(rcPath)) {
|
|
1065
|
+
rc = await fs.readJson(rcPath);
|
|
1066
|
+
}
|
|
1067
|
+
// Resolve worktree path
|
|
1068
|
+
const base = rc.worktreeBase ?? path.dirname(repoRoot);
|
|
1069
|
+
const worktreePath = path.join(base, `${repoName(repoRoot)}-${featureName}`);
|
|
1070
|
+
// Check state file for existing entry
|
|
1071
|
+
const stateFile = stateFilePath$1(repoRoot);
|
|
1072
|
+
const entries = await readStateFile(stateFile);
|
|
1073
|
+
const existing = entries.find(e => e.featureName === featureName);
|
|
1074
|
+
if (existing) {
|
|
1075
|
+
console.log(`\nWorktree for "${featureName}" already exists at: ${existing.worktreePath}`);
|
|
1076
|
+
const answer = await prompt('Resume or Abort? [R/a] ');
|
|
1077
|
+
if (answer.toLowerCase() === 'a') {
|
|
1078
|
+
console.log('Aborted.');
|
|
1079
|
+
process.exit(0);
|
|
1080
|
+
}
|
|
1081
|
+
console.log(`\nResuming existing worktree.`);
|
|
1082
|
+
console.log(` Branch: ${existing.branch}`);
|
|
1083
|
+
console.log(` Worktree path: ${existing.worktreePath}`);
|
|
1084
|
+
console.log(` Working dir: ${existing.workingDir}`);
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
await createWorktree(branch, worktreePath, repoRoot);
|
|
1088
|
+
// Resolve working dir
|
|
1089
|
+
let projectPath;
|
|
1090
|
+
if (rc.repoType === 'monorepo') {
|
|
1091
|
+
const pkg = options.dir ?? await prompt('Package path (e.g. packages/my-package): ');
|
|
1092
|
+
projectPath = path.join(worktreePath, pkg);
|
|
1093
|
+
}
|
|
1094
|
+
else {
|
|
1095
|
+
projectPath = worktreePath;
|
|
1096
|
+
}
|
|
1097
|
+
const workingDir = path.join(projectPath, 'working-on', featureName);
|
|
1098
|
+
await fs.ensureDir(workingDir);
|
|
1099
|
+
// Write state file entry
|
|
1100
|
+
entries.push({ featureName, branch, worktreePath, workingDir, repoRoot });
|
|
1101
|
+
await writeStateFile(stateFile, entries);
|
|
1102
|
+
console.log(`\nWorktree feature started:`);
|
|
1103
|
+
console.log(` Branch: ${branch}`);
|
|
1104
|
+
console.log(` Worktree path: ${worktreePath}`);
|
|
1105
|
+
console.log(` Working dir: ${workingDir}`);
|
|
1106
|
+
}
|
|
1107
|
+
function startWorktreeCommand() {
|
|
1108
|
+
const cmd = new Command('start-worktree');
|
|
1109
|
+
cmd
|
|
1110
|
+
.description('Start a new feature using a git worktree')
|
|
1111
|
+
.argument('<feature-name>', 'feature name (kebab-case, max 30 chars)')
|
|
1112
|
+
.option('-d, --dir <package>', 'package path within monorepo (skips prompt)')
|
|
1113
|
+
.action(async (featureName, options) => {
|
|
1114
|
+
await runStartWorktree(featureName, options);
|
|
1115
|
+
});
|
|
1116
|
+
return cmd;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function resolveRepoRoot(cwd) {
|
|
1120
|
+
return execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim();
|
|
1121
|
+
}
|
|
1122
|
+
function stateFilePath(repoRoot) {
|
|
1123
|
+
return path.join(path.dirname(repoRoot), '.osddt-worktrees');
|
|
1124
|
+
}
|
|
1125
|
+
async function runWorktreeInfo(featureName) {
|
|
1126
|
+
const repoRoot = resolveRepoRoot(process.cwd());
|
|
1127
|
+
const stateFile = stateFilePath(repoRoot);
|
|
1128
|
+
if (!(await fs.pathExists(stateFile))) {
|
|
1129
|
+
console.error(`No .osddt-worktrees file found at: ${stateFile}`);
|
|
1130
|
+
process.exit(1);
|
|
1131
|
+
}
|
|
1132
|
+
const entries = await fs.readJson(stateFile);
|
|
1133
|
+
const entry = entries.find(e => e.featureName === featureName);
|
|
1134
|
+
if (!entry) {
|
|
1135
|
+
console.error(`No worktree entry found for feature: ${featureName}`);
|
|
1136
|
+
process.exit(1);
|
|
1137
|
+
}
|
|
1138
|
+
console.log(JSON.stringify({ worktreePath: entry.worktreePath, workingDir: entry.workingDir, branch: entry.branch }));
|
|
1139
|
+
}
|
|
1140
|
+
function worktreeInfoCommand() {
|
|
1141
|
+
const cmd = new Command('worktree-info');
|
|
1142
|
+
cmd
|
|
1143
|
+
.description('Look up worktree paths for a feature from the state file')
|
|
1144
|
+
.argument('<feature-name>', 'feature name to look up')
|
|
1145
|
+
.action(async (featureName) => {
|
|
1146
|
+
await runWorktreeInfo(featureName);
|
|
1147
|
+
});
|
|
1148
|
+
return cmd;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
function checkGitVersion() {
|
|
1152
|
+
try {
|
|
1153
|
+
const output = execSync('git --version', { encoding: 'utf-8' }).trim();
|
|
1154
|
+
// e.g. "git version 2.39.3"
|
|
1155
|
+
const match = output.match(/git version (\d+)\.(\d+)/);
|
|
1156
|
+
if (!match) {
|
|
1157
|
+
return { label: 'Git version >= 2.5', passed: false, detail: `Could not parse git version: ${output}` };
|
|
1158
|
+
}
|
|
1159
|
+
const major = parseInt(match[1], 10);
|
|
1160
|
+
const minor = parseInt(match[2], 10);
|
|
1161
|
+
const ok = major > 2 || (major === 2 && minor >= 5);
|
|
1162
|
+
return {
|
|
1163
|
+
label: 'Git version >= 2.5',
|
|
1164
|
+
passed: ok,
|
|
1165
|
+
detail: ok ? output : `Found ${output} — git worktree requires >= 2.5`,
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
catch {
|
|
1169
|
+
return { label: 'Git version >= 2.5', passed: false, detail: 'git not found or not executable' };
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
function checkNotAWorktree(cwd) {
|
|
1173
|
+
try {
|
|
1174
|
+
const gitCommonDir = execSync('git rev-parse --git-common-dir', { cwd, encoding: 'utf-8' }).trim();
|
|
1175
|
+
const gitDir = execSync('git rev-parse --git-dir', { cwd, encoding: 'utf-8' }).trim();
|
|
1176
|
+
const isWorktree = gitDir !== gitCommonDir && gitDir !== '.git';
|
|
1177
|
+
return {
|
|
1178
|
+
label: 'Current directory is not a worktree',
|
|
1179
|
+
passed: !isWorktree,
|
|
1180
|
+
detail: isWorktree
|
|
1181
|
+
? `This directory is itself a worktree (git-dir: ${gitDir}). Run setup-worktree from the main repository.`
|
|
1182
|
+
: 'OK',
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
catch {
|
|
1186
|
+
return { label: 'Current directory is not a worktree', passed: false, detail: 'Not inside a git repository' };
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
async function checkTargetWritable(cwd) {
|
|
1190
|
+
let targetBase;
|
|
1191
|
+
try {
|
|
1192
|
+
const repoRoot = execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim();
|
|
1193
|
+
const rcPath = path.join(repoRoot, '.osddtrc');
|
|
1194
|
+
if (await fs.pathExists(rcPath)) {
|
|
1195
|
+
const rc = await fs.readJson(rcPath);
|
|
1196
|
+
targetBase = rc.worktreeBase ?? path.dirname(repoRoot);
|
|
1197
|
+
}
|
|
1198
|
+
else {
|
|
1199
|
+
targetBase = path.dirname(repoRoot);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
catch {
|
|
1203
|
+
return { label: 'Worktree target directory is writable', passed: false, detail: 'Could not resolve repo root' };
|
|
1204
|
+
}
|
|
1205
|
+
try {
|
|
1206
|
+
await fs.access(targetBase, fs.constants.W_OK);
|
|
1207
|
+
return { label: 'Worktree target directory is writable', passed: true, detail: `${targetBase} is writable` };
|
|
1208
|
+
}
|
|
1209
|
+
catch {
|
|
1210
|
+
return { label: 'Worktree target directory is writable', passed: false, detail: `${targetBase} is not writable` };
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
function printResult(result) {
|
|
1214
|
+
const icon = result.passed ? '✓' : '✗';
|
|
1215
|
+
console.log(` ${icon} ${result.label}`);
|
|
1216
|
+
if (!result.passed) {
|
|
1217
|
+
console.log(` → ${result.detail}`);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
async function runSetupWorktree(cwd) {
|
|
1221
|
+
console.log('Checking environment for git worktree support...\n');
|
|
1222
|
+
const results = [
|
|
1223
|
+
checkGitVersion(),
|
|
1224
|
+
checkNotAWorktree(cwd),
|
|
1225
|
+
await checkTargetWritable(cwd),
|
|
1226
|
+
];
|
|
1227
|
+
for (const result of results) {
|
|
1228
|
+
printResult(result);
|
|
1229
|
+
}
|
|
1230
|
+
const allPassed = results.every(r => r.passed);
|
|
1231
|
+
console.log('');
|
|
1232
|
+
if (allPassed) {
|
|
1233
|
+
console.log('All checks passed. You can use the worktree workflow.');
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
console.log('Some checks failed. Resolve the issues above before using the worktree workflow.');
|
|
1237
|
+
process.exit(1);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
function setupWorktreeCommand() {
|
|
1241
|
+
const cmd = new Command('setup-worktree');
|
|
1242
|
+
cmd
|
|
1243
|
+
.description('Validate the environment for git worktree usage')
|
|
1244
|
+
.option('-d, --dir <directory>', 'directory to check', process.cwd())
|
|
1245
|
+
.action(async (options) => {
|
|
1246
|
+
await runSetupWorktree(path.resolve(options.dir));
|
|
1247
|
+
});
|
|
1248
|
+
return cmd;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
871
1251
|
const __filename$1 = fileURLToPath(import.meta.url);
|
|
872
1252
|
const __dirname$1 = path.dirname(__filename$1);
|
|
873
1253
|
const pkgPath = path.join(__dirname$1, '..', 'package.json');
|
|
@@ -882,4 +1262,7 @@ program.addCommand(metaInfoCommand());
|
|
|
882
1262
|
program.addCommand(doneCommand());
|
|
883
1263
|
program.addCommand(updateCommand());
|
|
884
1264
|
program.addCommand(contextCommand());
|
|
1265
|
+
program.addCommand(startWorktreeCommand());
|
|
1266
|
+
program.addCommand(worktreeInfoCommand());
|
|
1267
|
+
program.addCommand(setupWorktreeCommand());
|
|
885
1268
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dezkareid/osddt",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "Package for Spec-Driven Development workflow",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spec-driven",
|
|
@@ -42,13 +42,12 @@
|
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@commitlint/cli": "20.5.0",
|
|
44
44
|
"@commitlint/config-conventional": "20.5.0",
|
|
45
|
-
"@dezkareid/eslint-
|
|
45
|
+
"@dezkareid/eslint-plugin-web": "1.0.0",
|
|
46
46
|
"@rollup/plugin-typescript": "12.1.4",
|
|
47
47
|
"@types/fs-extra": "11.0.4",
|
|
48
48
|
"@types/node": "25.0.10",
|
|
49
49
|
"eslint": "9.39.2",
|
|
50
50
|
"husky": "9.1.7",
|
|
51
|
-
"prettier": "3.8.1",
|
|
52
51
|
"rollup": "4.56.0",
|
|
53
52
|
"typescript": "5.9.3",
|
|
54
53
|
"vitest": "4.0.18"
|
|
@@ -59,7 +58,7 @@
|
|
|
59
58
|
"test": "vitest run",
|
|
60
59
|
"test:watch": "vitest",
|
|
61
60
|
"lint": "eslint src",
|
|
62
|
-
"
|
|
61
|
+
"lint:fix": "eslint src --fix",
|
|
63
62
|
"setup-local": "pnpm run build && npx osddt setup --agents claude --repo-type single",
|
|
64
63
|
"update-local": "pnpm run build && npx osddt update"
|
|
65
64
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|