@dezkareid/osddt 1.10.0 → 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 +401 -13
- package/package.json +8 -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
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import path from 'path';
|
|
4
3
|
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
5
6
|
import select from '@inquirer/select';
|
|
6
7
|
import checkbox from '@inquirer/checkbox';
|
|
7
8
|
import { execSync } from 'child_process';
|
|
9
|
+
import readline from 'readline';
|
|
8
10
|
|
|
9
11
|
function getRepoPreamble(npxCommand) {
|
|
10
12
|
return `## Context
|
|
@@ -120,6 +122,15 @@ const COMMAND_DEFINITIONS = [
|
|
|
120
122
|
|
|
121
123
|
## Instructions
|
|
122
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
|
+
|
|
123
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:
|
|
124
135
|
|
|
125
136
|
| Condition | Current phase | Run next |
|
|
@@ -221,6 +232,45 @@ ${getCustomContextStep(npxCommand, 'start')}## Arguments
|
|
|
221
232
|
|
|
222
233
|
${args}
|
|
223
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
|
+
|
|
224
274
|
${getNextStepToSpec(args)}
|
|
225
275
|
`,
|
|
226
276
|
},
|
|
@@ -501,16 +551,53 @@ ${args}
|
|
|
501
551
|
body: ({ npxCommand }) => `## Instructions
|
|
502
552
|
|
|
503
553
|
1. Confirm all tasks in \`osddt.tasks.md\` are checked off (\`- [x]\`)
|
|
504
|
-
2. Run the following command to
|
|
554
|
+
2. Run the following command to check whether this feature uses a git worktree:
|
|
505
555
|
|
|
506
556
|
\`\`\`
|
|
507
|
-
${npxCommand}
|
|
557
|
+
${npxCommand} worktree-info <feature-name>
|
|
508
558
|
\`\`\`
|
|
509
559
|
|
|
510
|
-
|
|
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.
|
|
511
598
|
For example, \`working-on/feature-a\` will be moved to \`done/YYYY-MM-DD-feature-a\`.
|
|
512
599
|
|
|
513
|
-
|
|
600
|
+
9. Report the result of the command, including the full destination path
|
|
514
601
|
|
|
515
602
|
${getCustomContextStep(npxCommand, 'done')}`,
|
|
516
603
|
},
|
|
@@ -526,7 +613,7 @@ ${body}`;
|
|
|
526
613
|
}
|
|
527
614
|
function getClaudeTemplates(cwd, npxCommand) {
|
|
528
615
|
const dir = path.join(cwd, CLAUDE_COMMANDS_DIR);
|
|
529
|
-
return COMMAND_DEFINITIONS.map(
|
|
616
|
+
return COMMAND_DEFINITIONS.map(cmd => ({
|
|
530
617
|
filePath: path.join(dir, `${cmd.name}.md`),
|
|
531
618
|
content: formatClaudeCommand(cmd.description, cmd.body({ args: '$ARGUMENTS', npxCommand })),
|
|
532
619
|
}));
|
|
@@ -542,7 +629,7 @@ ${body}"""
|
|
|
542
629
|
}
|
|
543
630
|
function getGeminiTemplates(cwd, npxCommand) {
|
|
544
631
|
const dir = path.join(cwd, GEMINI_COMMANDS_DIR);
|
|
545
|
-
return COMMAND_DEFINITIONS.map(
|
|
632
|
+
return COMMAND_DEFINITIONS.map(cmd => ({
|
|
546
633
|
filePath: path.join(dir, `${cmd.name}.toml`),
|
|
547
634
|
content: formatGeminiCommand(cmd.description, cmd.body({ args: '{{args}}', npxCommand })),
|
|
548
635
|
}));
|
|
@@ -607,12 +694,12 @@ async function resolveNpxCommand(cwd) {
|
|
|
607
694
|
const VALID_AGENTS = ['claude', 'gemini'];
|
|
608
695
|
const VALID_REPO_TYPES = ['single', 'monorepo'];
|
|
609
696
|
function parseAgents(raw) {
|
|
610
|
-
const values = raw.split(',').map(
|
|
697
|
+
const values = raw.split(',').map(s => s.trim());
|
|
611
698
|
if (values.length === 0) {
|
|
612
699
|
console.error('Error: --agents requires at least one value.');
|
|
613
700
|
process.exit(1);
|
|
614
701
|
}
|
|
615
|
-
const invalid = values.filter(
|
|
702
|
+
const invalid = values.filter(v => !VALID_AGENTS.includes(v));
|
|
616
703
|
if (invalid.length > 0) {
|
|
617
704
|
console.error(`Error: Invalid agent(s): ${invalid.join(', ')}. Valid values: ${VALID_AGENTS.join(', ')}.`);
|
|
618
705
|
process.exit(1);
|
|
@@ -711,7 +798,13 @@ function todayPrefix() {
|
|
|
711
798
|
const dd = String(now.getDate()).padStart(2, '0');
|
|
712
799
|
return `${yyyy}-${mm}-${dd}`;
|
|
713
800
|
}
|
|
714
|
-
|
|
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) {
|
|
715
808
|
const src = path.join(cwd, 'working-on', featureName);
|
|
716
809
|
const destName = `${todayPrefix()}-${featureName}`;
|
|
717
810
|
const dest = path.join(cwd, 'done', destName);
|
|
@@ -722,6 +815,30 @@ async function runDone(featureName, cwd) {
|
|
|
722
815
|
await fs.ensureDir(path.dirname(dest));
|
|
723
816
|
await fs.move(src, dest);
|
|
724
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`);
|
|
725
842
|
}
|
|
726
843
|
function doneCommand() {
|
|
727
844
|
const cmd = new Command('done');
|
|
@@ -729,9 +846,10 @@ function doneCommand() {
|
|
|
729
846
|
.description('Move a feature from working-on/<feature-name> to done/<feature-name>')
|
|
730
847
|
.argument('<feature-name>', 'name of the feature to mark as done')
|
|
731
848
|
.option('-d, --dir <directory>', 'project directory', process.cwd())
|
|
849
|
+
.option('--worktree', 'also remove the git worktree and clean up the state file')
|
|
732
850
|
.action(async (featureName, options) => {
|
|
733
851
|
const targetDir = path.resolve(options.dir);
|
|
734
|
-
await runDone(featureName, targetDir);
|
|
852
|
+
await runDone(featureName, targetDir, options.worktree ?? false);
|
|
735
853
|
});
|
|
736
854
|
return cmd;
|
|
737
855
|
}
|
|
@@ -744,7 +862,7 @@ async function hasOsddtCommandFile(dir, pattern) {
|
|
|
744
862
|
if (!(await fs.pathExists(dir)))
|
|
745
863
|
return false;
|
|
746
864
|
const entries = await fs.readdir(dir);
|
|
747
|
-
return entries.some(
|
|
865
|
+
return entries.some(f => pattern.test(f));
|
|
748
866
|
}
|
|
749
867
|
async function inferAgents(cwd) {
|
|
750
868
|
const detected = [];
|
|
@@ -867,14 +985,284 @@ function contextCommand() {
|
|
|
867
985
|
return cmd;
|
|
868
986
|
}
|
|
869
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
|
+
|
|
1251
|
+
const __filename$1 = fileURLToPath(import.meta.url);
|
|
1252
|
+
const __dirname$1 = path.dirname(__filename$1);
|
|
1253
|
+
const pkgPath = path.join(__dirname$1, '..', 'package.json');
|
|
1254
|
+
const pkg = fs.readJsonSync(pkgPath);
|
|
870
1255
|
const program = new Command();
|
|
871
1256
|
program
|
|
872
1257
|
.name('osddt')
|
|
873
1258
|
.description('Spec-Driven Development tooling for monorepos and single-package repos')
|
|
874
|
-
.version(
|
|
1259
|
+
.version(pkg.version);
|
|
875
1260
|
program.addCommand(setupCommand());
|
|
876
1261
|
program.addCommand(metaInfoCommand());
|
|
877
1262
|
program.addCommand(doneCommand());
|
|
878
1263
|
program.addCommand(updateCommand());
|
|
879
1264
|
program.addCommand(contextCommand());
|
|
1265
|
+
program.addCommand(startWorktreeCommand());
|
|
1266
|
+
program.addCommand(worktreeInfoCommand());
|
|
1267
|
+
program.addCommand(setupWorktreeCommand());
|
|
880
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",
|
|
@@ -40,11 +40,14 @@
|
|
|
40
40
|
"tslib": "2.8.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
+
"@commitlint/cli": "20.5.0",
|
|
44
|
+
"@commitlint/config-conventional": "20.5.0",
|
|
45
|
+
"@dezkareid/eslint-plugin-web": "1.0.0",
|
|
43
46
|
"@rollup/plugin-typescript": "12.1.4",
|
|
44
47
|
"@types/fs-extra": "11.0.4",
|
|
45
48
|
"@types/node": "25.0.10",
|
|
46
49
|
"eslint": "9.39.2",
|
|
47
|
-
"
|
|
50
|
+
"husky": "9.1.7",
|
|
48
51
|
"rollup": "4.56.0",
|
|
49
52
|
"typescript": "5.9.3",
|
|
50
53
|
"vitest": "4.0.18"
|
|
@@ -55,7 +58,8 @@
|
|
|
55
58
|
"test": "vitest run",
|
|
56
59
|
"test:watch": "vitest",
|
|
57
60
|
"lint": "eslint src",
|
|
58
|
-
"
|
|
59
|
-
"setup-local": "pnpm run build && npx osddt setup --agents claude --repo-type single"
|
|
61
|
+
"lint:fix": "eslint src --fix",
|
|
62
|
+
"setup-local": "pnpm run build && npx osddt setup --agents claude --repo-type single",
|
|
63
|
+
"update-local": "pnpm run build && npx osddt update"
|
|
60
64
|
}
|
|
61
65
|
}
|
|
@@ -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 {};
|