@dezkareid/osddt 1.3.1 → 1.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/AGENTS.md +44 -16
- package/README.md +6 -5
- package/dist/index.js +124 -62
- package/dist/templates/claude.d.ts +1 -1
- package/dist/templates/gemini.d.ts +1 -1
- package/dist/templates/shared.d.ts +7 -2
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -131,14 +131,29 @@ osddt/
|
|
|
131
131
|
|
|
132
132
|
### CLI Commands
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
#### Invocation forms
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
|
139
|
-
|
|
|
140
|
-
|
|
|
141
|
-
|
|
|
136
|
+
There are two contexts in which osddt commands are invoked:
|
|
137
|
+
|
|
138
|
+
| Context | Command prefix | When to use |
|
|
139
|
+
| ------- | -------------- | ----------- |
|
|
140
|
+
| **Local development of this package** | `osddt` | Working inside this repository — the binary is available directly because the package is installed locally |
|
|
141
|
+
| **External project** (using osddt as a dependency) | `npx @dezkareid/osddt` | Running from a project that lists `@dezkareid/osddt` as a dependency |
|
|
142
|
+
|
|
143
|
+
When `osddt setup` is run, it reads the `name` field of `package.json` in the target directory. If the name is `@dezkareid/osddt`, templates are written with `npx osddt`. Otherwise they fall back to `npx @dezkareid/osddt`. The resolution lives in `resolveNpxCommand()` in `src/commands/setup.ts`.
|
|
144
|
+
|
|
145
|
+
#### Available commands
|
|
146
|
+
|
|
147
|
+
| Command | Context | Description |
|
|
148
|
+
| -------------------------------------------------------------------- | ------------- | ------------------------------------------------------------- |
|
|
149
|
+
| `osddt setup` | Local dev | Generate agent command files for Claude and Gemini |
|
|
150
|
+
| `osddt setup --agents <list> --repo-type <type>` | Local dev | Non-interactive setup (for CI/scripted environments) |
|
|
151
|
+
| `npx @dezkareid/osddt setup` | External | Generate agent command files for Claude and Gemini |
|
|
152
|
+
| `npx @dezkareid/osddt setup --agents <list> --repo-type <type>` | External | Non-interactive setup (for CI/scripted environments) |
|
|
153
|
+
| `osddt meta-info` | Local dev | Output current branch and date as JSON |
|
|
154
|
+
| `npx @dezkareid/osddt meta-info` | External | Output current branch and date as JSON |
|
|
155
|
+
| `osddt done <feature-name> --dir <project-path>` | Local dev | Move `working-on/<feature>` to `done/<feature>` |
|
|
156
|
+
| `npx @dezkareid/osddt done <feature-name> --dir <project-path>` | External | Move `working-on/<feature>` to `done/<feature>` |
|
|
142
157
|
|
|
143
158
|
#### `osddt setup` options
|
|
144
159
|
|
|
@@ -160,6 +175,7 @@ Templates are generated by `npx @dezkareid/osddt setup` and placed in each agent
|
|
|
160
175
|
| `osddt.research` | Research a topic and write a research file to inform the spec |
|
|
161
176
|
| `osddt.start` | Start a new feature by creating a branch and working-on folder |
|
|
162
177
|
| `osddt.spec` | Analyze requirements and write a feature specification |
|
|
178
|
+
| `osddt.clarify` | Resolve open questions in the spec and record decisions (optional) |
|
|
163
179
|
| `osddt.plan` | Create a technical implementation plan from a specification |
|
|
164
180
|
| `osddt.tasks` | Generate actionable tasks from an implementation plan |
|
|
165
181
|
| `osddt.implement` | Execute tasks from the task list one by one |
|
|
@@ -167,20 +183,21 @@ Templates are generated by `npx @dezkareid/osddt setup` and placed in each agent
|
|
|
167
183
|
|
|
168
184
|
#### Template Workflow
|
|
169
185
|
|
|
170
|
-
`osddt.research` and `osddt.start` are **peer entry points** — use whichever fits your situation. Both lead to `osddt.spec`.
|
|
186
|
+
`osddt.research` and `osddt.start` are **peer entry points** — use whichever fits your situation. Both lead to `osddt.spec`. If you close the coding session, you should execute the `osddt.continue` command to resume the workflow.
|
|
171
187
|
|
|
172
188
|
```
|
|
173
|
-
osddt.continue
|
|
174
|
-
|
|
175
|
-
osddt.research ──┐
|
|
176
|
-
├──► osddt.spec → osddt.plan → osddt.tasks → osddt.implement → osddt.done ◄─┘
|
|
189
|
+
osddt.continue ──────────────────────────────────────────────────────────────────────────────────────┐
|
|
190
|
+
│
|
|
191
|
+
osddt.research ──┐ │
|
|
192
|
+
├──► osddt.spec → [osddt.clarify] → osddt.plan → osddt.tasks → osddt.implement → osddt.done ◄─┘
|
|
177
193
|
osddt.start ──┘
|
|
178
194
|
```
|
|
179
195
|
|
|
180
|
-
- Use **`osddt.continue`**
|
|
196
|
+
- Use **`osddt.continue`** if you closed the coding session to detect the current phase and get the exact command to run next. It inspects the `working-on/<feature-name>/` folder for phase files and reports which one was found.
|
|
181
197
|
- 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`.
|
|
182
198
|
- Use **`osddt.start`** when you are ready to begin implementation directly. It creates the git branch and the `working-on/<feature-name>/` folder.
|
|
183
199
|
- Both `osddt.research` and `osddt.start` check whether the working directory already exists and ask to **Resume** or **Abort** if it does.
|
|
200
|
+
- Use **`osddt.clarify`** (optional) to resolve any Open Questions in the spec before planning. It can be invoked at any point in the workflow; after each session it always prompts to run (or re-run) `osddt.plan`.
|
|
184
201
|
|
|
185
202
|
#### Generated File Locations
|
|
186
203
|
|
|
@@ -195,7 +212,8 @@ osddt.start ──┘
|
|
|
195
212
|
- **Actions performed by the agent**:
|
|
196
213
|
1. Runs `npx @dezkareid/osddt meta-info` and reads `.osddtrc` to resolve the project path.
|
|
197
214
|
2. Checks the `working-on/<feature-name>/` folder for the following phase files **in order**: `osddt.tasks.md` (with unchecked tasks), `osddt.tasks.md` (all checked), `osddt.plan.md`, `osddt.spec.md`, `osddt.research.md`.
|
|
198
|
-
3. Reports the file found, the current phase, and the **exact command** the user should run next.
|
|
215
|
+
3. Reports the file found, the current phase, and the **exact command** the user should run next. Commands that require no further arguments (`/osddt.implement`, `/osddt.done`, `/osddt.tasks`) are suggested without arguments; commands that still need feature context (`/osddt.plan`, `/osddt.spec`, `/osddt.research`, `/osddt.clarify`) are suggested with the feature name.
|
|
216
|
+
4. If the detected phase is **Spec done** or **Planning done** and the spec has unanswered open questions, additionally recommends running `/osddt.clarify`.
|
|
199
217
|
|
|
200
218
|
#### osddt.start behaviour
|
|
201
219
|
|
|
@@ -214,12 +232,22 @@ osddt.start ──┘
|
|
|
214
232
|
2. Checks for an existing `working-on/<feature-name>/` folder — offers **Resume** or **Abort** if found, otherwise creates it.
|
|
215
233
|
3. Researches the topic (codebase exploration, external references) and writes `osddt.research.md`.
|
|
216
234
|
|
|
235
|
+
#### osddt.clarify behaviour
|
|
236
|
+
|
|
237
|
+
- **Input**: A feature name or branch name identifying the feature whose spec to clarify.
|
|
238
|
+
- **Actions performed by the agent**:
|
|
239
|
+
1. Locates `osddt.spec.md` in `working-on/<feature-name>/`; if absent, suggests running `osddt.spec` first.
|
|
240
|
+
2. Reads the **Open Questions** section and the **Decisions** section (if present) to determine which questions are already answered.
|
|
241
|
+
3. Informs the user of any already-resolved questions and only asks the remaining unanswered ones.
|
|
242
|
+
4. Records all new answers in a `## Decisions` section of `osddt.spec.md` (the Open Questions section is left unchanged).
|
|
243
|
+
5. Always prompts the user to run (or re-run) `osddt.plan` to reflect the updated decisions.
|
|
244
|
+
|
|
217
245
|
#### osddt.done behaviour
|
|
218
246
|
|
|
219
|
-
- **Input**:
|
|
247
|
+
- **Input**: None — the feature is identified automatically.
|
|
220
248
|
- **Actions performed by the agent**:
|
|
221
249
|
1. Reads `.osddtrc` to resolve the project path (single vs monorepo). For monorepos, asks the user which package.
|
|
222
|
-
2.
|
|
250
|
+
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.
|
|
223
251
|
3. Confirms all tasks in `osddt.tasks.md` are checked off (`- [x]`).
|
|
224
252
|
4. Runs `npx @dezkareid/osddt done <feature-name> --dir <project-path>` to move the folder.
|
|
225
253
|
|
package/README.md
CHANGED
|
@@ -32,13 +32,13 @@ npx @dezkareid/osddt setup --agents claude,gemini --repo-type single
|
|
|
32
32
|
|
|
33
33
|
Run `npx @dezkareid/osddt setup` once to generate the agent command files.
|
|
34
34
|
|
|
35
|
-
`osddt.research` and `osddt.start` are **peer entry points** — use whichever fits your situation. Both lead to `osddt.spec`.
|
|
35
|
+
`osddt.research` and `osddt.start` are **peer entry points** — use whichever fits your situation. Both lead to `osddt.spec`. If you close the coding session, you should execute the `osddt.continue` command to resume the workflow.
|
|
36
36
|
|
|
37
37
|
```
|
|
38
|
-
osddt.continue
|
|
39
|
-
|
|
40
|
-
osddt.research ──┐
|
|
41
|
-
├──► osddt.spec → osddt.plan → osddt.tasks → osddt.implement → osddt.done ◄─┘
|
|
38
|
+
osddt.continue ──────────────────────────────────────────────────────────────────────────────────────┐
|
|
39
|
+
│
|
|
40
|
+
osddt.research ──┐ │
|
|
41
|
+
├──► osddt.spec → [osddt.clarify] → osddt.plan → osddt.tasks → osddt.implement → osddt.done ◄─┘
|
|
42
42
|
osddt.start ──┘
|
|
43
43
|
```
|
|
44
44
|
|
|
@@ -48,6 +48,7 @@ osddt.start ──┘
|
|
|
48
48
|
| `osddt.research` | Research a topic and write a research file to inform the spec |
|
|
49
49
|
| `osddt.start` | Start a new feature by creating a branch and working-on folder |
|
|
50
50
|
| `osddt.spec` | Analyze requirements and write a feature specification |
|
|
51
|
+
| `osddt.clarify` | Resolve open questions in the spec and record decisions (optional) |
|
|
51
52
|
| `osddt.plan` | Create a technical implementation plan from a specification |
|
|
52
53
|
| `osddt.tasks` | Generate actionable tasks from an implementation plan |
|
|
53
54
|
| `osddt.implement` | Execute tasks from the task list one by one |
|
package/dist/index.js
CHANGED
|
@@ -6,12 +6,13 @@ import select from '@inquirer/select';
|
|
|
6
6
|
import checkbox from '@inquirer/checkbox';
|
|
7
7
|
import { execSync } from 'child_process';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
function getRepoPreamble(npxCommand) {
|
|
10
|
+
return `## Context
|
|
10
11
|
|
|
11
12
|
Before proceeding, run the following command and parse the JSON output to get the current branch and date:
|
|
12
13
|
|
|
13
14
|
\`\`\`
|
|
14
|
-
|
|
15
|
+
${npxCommand} meta-info
|
|
15
16
|
\`\`\`
|
|
16
17
|
|
|
17
18
|
## Repository Configuration
|
|
@@ -28,11 +29,12 @@ Before proceeding, read the \`.osddtrc\` file in the root of the repository to d
|
|
|
28
29
|
|
|
29
30
|
## Working Directory
|
|
30
31
|
|
|
31
|
-
All generated files live under \`<project-path>/working-on/<feature-name>/\`.
|
|
32
|
+
All generated files live under \`<project-path>/working-on/<feature-name>/\`.
|
|
32
33
|
|
|
33
34
|
> All file paths in the instructions below are relative to \`<project-path>/working-on/<feature-name>/\`.
|
|
34
35
|
|
|
35
36
|
`;
|
|
37
|
+
}
|
|
36
38
|
const FEATURE_NAME_RULES = `### Feature Name Constraints
|
|
37
39
|
|
|
38
40
|
When deriving a feature name from a description:
|
|
@@ -62,25 +64,41 @@ const WORKING_DIR_STEP = `Check whether the working directory \`<project-path>/w
|
|
|
62
64
|
- If it **already exists**, warn the user and ask whether to:
|
|
63
65
|
- **Resume** — continue into the existing folder (proceed to the next step without recreating it)
|
|
64
66
|
- **Abort** — stop and do nothing`;
|
|
67
|
+
const RESOLVE_FEATURE_NAME = `### Resolving the Feature Name
|
|
68
|
+
|
|
69
|
+
Use the following logic to determine \`<feature-name>\`:
|
|
70
|
+
|
|
71
|
+
1. If arguments were provided, derive the feature name from them:
|
|
72
|
+
- If the argument looks like a branch name (no spaces, kebab-case or slash-separated), use the last segment (after the last \`/\`, or the full value if no \`/\` is present).
|
|
73
|
+
- Otherwise treat it as a human-readable description and convert it to a feature name following the constraints in the Feature Name Constraints section.
|
|
74
|
+
2. If **no arguments were provided**:
|
|
75
|
+
- List all folders under \`<project-path>/working-on/\`.
|
|
76
|
+
- If there is **only one folder**, use it automatically and inform the user.
|
|
77
|
+
- If there are **multiple folders**, present the list to the user and ask them to pick one.
|
|
78
|
+
- If there are **no folders**, inform the user that no in-progress features were found and stop.`;
|
|
65
79
|
const COMMAND_DEFINITIONS = [
|
|
66
80
|
{
|
|
67
81
|
name: 'osddt.continue',
|
|
68
82
|
description: 'Detect the current workflow phase and prompt the next command to run',
|
|
69
|
-
body: (args) => `${
|
|
83
|
+
body: ({ args, npxCommand }) => `${getRepoPreamble(npxCommand)}${RESOLVE_FEATURE_NAME}
|
|
84
|
+
|
|
85
|
+
## Instructions
|
|
70
86
|
|
|
71
87
|
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:
|
|
72
88
|
|
|
73
89
|
| Condition | Current phase | Run next |
|
|
74
90
|
| --------- | ------------- | -------- |
|
|
75
|
-
| \`osddt.tasks.md\` exists **and** has at least one unchecked task (\`- [ ]\`) | Implementing | \`/osddt.implement
|
|
76
|
-
| \`osddt.tasks.md\` exists **and** all tasks are checked (\`- [x]\`) | Ready to close | \`/osddt.done
|
|
77
|
-
| \`osddt.plan.md\` exists | Planning done | \`/osddt.tasks
|
|
78
|
-
| \`osddt.spec.md\` exists | Spec done | \`/osddt.plan
|
|
79
|
-
| \`osddt.research.md\` exists | Research done | \`/osddt.spec
|
|
80
|
-
| None of the above | Not started | \`/osddt.spec
|
|
91
|
+
| \`osddt.tasks.md\` exists **and** has at least one unchecked task (\`- [ ]\`) | Implementing | \`/osddt.implement\` |
|
|
92
|
+
| \`osddt.tasks.md\` exists **and** all tasks are checked (\`- [x]\`) | Ready to close | \`/osddt.done\` |
|
|
93
|
+
| \`osddt.plan.md\` exists | Planning done | \`/osddt.tasks\` |
|
|
94
|
+
| \`osddt.spec.md\` exists | Spec done | \`/osddt.plan <tech stack and key technical decisions>\` |
|
|
95
|
+
| \`osddt.research.md\` exists | Research done | \`/osddt.spec <brief feature description>\` |
|
|
96
|
+
| None of the above | Not started | \`/osddt.spec <brief feature description>\` (or \`/osddt.research <topic>\` if research is needed first) |
|
|
81
97
|
|
|
82
98
|
Report which file was found, which phase that corresponds to, and the exact command the user should run next.
|
|
83
99
|
|
|
100
|
+
> **Open Questions check**: After reporting the phase, if the detected phase is **Spec done** or **Planning done**, also check whether \`osddt.spec.md\` contains any unanswered open questions (items in the **Open Questions** section with no corresponding entry in the **Decisions** section). If unanswered questions exist, inform the user and recommend running \`/osddt.clarify <feature-name>\` before (or in addition to) the suggested next command.
|
|
101
|
+
|
|
84
102
|
## Arguments
|
|
85
103
|
|
|
86
104
|
${args}
|
|
@@ -89,7 +107,7 @@ ${args}
|
|
|
89
107
|
{
|
|
90
108
|
name: 'osddt.research',
|
|
91
109
|
description: 'Research a topic and write a research file to inform the feature specification',
|
|
92
|
-
body: (args) => `${
|
|
110
|
+
body: ({ args, npxCommand }) => `${getRepoPreamble(npxCommand)}## Instructions
|
|
93
111
|
|
|
94
112
|
The argument provided is: ${args}
|
|
95
113
|
|
|
@@ -132,14 +150,14 @@ ${args}
|
|
|
132
150
|
Run the following command to write the feature specification:
|
|
133
151
|
|
|
134
152
|
\`\`\`
|
|
135
|
-
/osddt.spec
|
|
153
|
+
/osddt.spec <brief description of the feature or topic researched>
|
|
136
154
|
\`\`\`
|
|
137
155
|
`,
|
|
138
156
|
},
|
|
139
157
|
{
|
|
140
158
|
name: 'osddt.start',
|
|
141
159
|
description: 'Start a new feature by creating a branch and working-on folder',
|
|
142
|
-
body: (args) => `${
|
|
160
|
+
body: ({ args, npxCommand }) => `${getRepoPreamble(npxCommand)}## Instructions
|
|
143
161
|
|
|
144
162
|
The argument provided is: ${args}
|
|
145
163
|
|
|
@@ -178,33 +196,35 @@ ${args}
|
|
|
178
196
|
Run the following command to write the feature specification:
|
|
179
197
|
|
|
180
198
|
\`\`\`
|
|
181
|
-
/osddt.spec
|
|
199
|
+
/osddt.spec <brief description of the feature being built>
|
|
182
200
|
\`\`\`
|
|
183
201
|
`,
|
|
184
202
|
},
|
|
185
203
|
{
|
|
186
204
|
name: 'osddt.spec',
|
|
187
205
|
description: 'Analyze requirements and write a feature specification',
|
|
188
|
-
body: (args) => `## Instructions
|
|
206
|
+
body: ({ args }) => `## Instructions
|
|
189
207
|
|
|
190
208
|
1. Check whether \`osddt.research.md\` exists in the working directory.
|
|
191
209
|
- If it exists, read it and use its findings (key insights, constraints, open questions, codebase findings) as additional context when writing the specification.
|
|
192
210
|
- If it does not exist, proceed using only the requirements provided in ${args}.
|
|
193
211
|
2. Analyze the requirements provided in ${args}
|
|
194
212
|
3. Identify the core problem being solved
|
|
195
|
-
4. Define the scope, constraints, and acceptance criteria
|
|
213
|
+
4. Define the scope, user-facing constraints, and acceptance criteria
|
|
196
214
|
5. Write the specification to \`osddt.spec.md\` in the working directory
|
|
197
215
|
|
|
198
216
|
## Specification Format
|
|
199
217
|
|
|
218
|
+
The spec should describe **what** the feature does and **why**, from a product and user perspective. Do **not** include implementation details, technology choices, or technical architecture — those belong in the plan.
|
|
219
|
+
|
|
200
220
|
The spec should include:
|
|
201
|
-
- **Overview**: What and why
|
|
202
|
-
- **Requirements**: Functional
|
|
203
|
-
- **Scope**: What is in and out of scope
|
|
204
|
-
- **Acceptance Criteria**: Clear, testable criteria
|
|
205
|
-
- **Open Questions**:
|
|
221
|
+
- **Overview**: What the feature is and why it is needed
|
|
222
|
+
- **Requirements**: Functional requirements only — what the system must do, expressed as user-observable behaviours
|
|
223
|
+
- **Scope**: What is in and out of scope, described in product terms
|
|
224
|
+
- **Acceptance Criteria**: Clear, testable criteria written from a user or business perspective
|
|
225
|
+
- **Open Questions**: Ambiguities about desired behaviour or product decisions to resolve
|
|
206
226
|
|
|
207
|
-
> If \`osddt.research.md\` was found, add a **Research Summary** section that briefly references the key insights and constraints it identified.
|
|
227
|
+
> If \`osddt.research.md\` was found, add a **Research Summary** section that briefly references the key insights and user-facing constraints it identified.
|
|
208
228
|
|
|
209
229
|
## Arguments
|
|
210
230
|
|
|
@@ -215,14 +235,49 @@ ${args}
|
|
|
215
235
|
Run the following command to create the implementation plan:
|
|
216
236
|
|
|
217
237
|
\`\`\`
|
|
218
|
-
/osddt.plan
|
|
238
|
+
/osddt.plan <tech stack and key technical decisions, e.g. "use Node.js with SQLite, REST API, no auth">
|
|
219
239
|
\`\`\`
|
|
240
|
+
`,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'osddt.clarify',
|
|
244
|
+
description: 'Resolve open questions in the spec and record decisions',
|
|
245
|
+
body: () => `## Instructions
|
|
246
|
+
|
|
247
|
+
1. Check whether \`osddt.spec.md\` exists in the working directory:
|
|
248
|
+
- If it **does not exist**, inform the user that no spec was found and suggest running \`/osddt.spec <brief feature description>\` first. Stop here.
|
|
249
|
+
|
|
250
|
+
2. Read \`osddt.spec.md\` and extract all items listed under the **Open Questions** section.
|
|
251
|
+
- If the **Open Questions** section is absent or empty, inform the user that there are no open questions to resolve. Skip to step 6.
|
|
252
|
+
|
|
253
|
+
3. Read the **Decisions** section of \`osddt.spec.md\` (if it exists) to determine which questions have already been answered.
|
|
254
|
+
- A question is considered answered if there is a corresponding numbered entry in the **Decisions** section.
|
|
255
|
+
- List the already-answered questions to the user and inform them they will be skipped.
|
|
256
|
+
|
|
257
|
+
4. For each **unanswered** question (in order), present it to the user and collect a response.
|
|
258
|
+
- If all questions were already answered, inform the user and skip to step 6.
|
|
259
|
+
|
|
260
|
+
5. Update the **Decisions** section in \`osddt.spec.md\`:
|
|
261
|
+
- If a **Decisions** section already exists, append new entries to it (do not modify existing entries).
|
|
262
|
+
- If no **Decisions** section exists, add one at the end of the file.
|
|
263
|
+
- Each decision entry uses the format: \`N. **<short question summary>**: <answer>\`
|
|
264
|
+
- The **Open Questions** section is left unchanged.
|
|
265
|
+
|
|
266
|
+
6. Inform the user that all questions are now resolved (or were already resolved). Then prompt them to run (or re-run) the plan step so it reflects the updated decisions:
|
|
267
|
+
|
|
268
|
+
\`\`\`
|
|
269
|
+
/osddt.plan <tech stack and key technical decisions, e.g. "use Node.js with SQLite, REST API, no auth">
|
|
270
|
+
\`\`\`
|
|
271
|
+
|
|
272
|
+
> Note: if \`osddt.plan.md\` already exists, the plan should be regenerated to incorporate the decisions.
|
|
220
273
|
`,
|
|
221
274
|
},
|
|
222
275
|
{
|
|
223
276
|
name: 'osddt.plan',
|
|
224
277
|
description: 'Create a technical implementation plan from a specification',
|
|
225
|
-
body: (args) =>
|
|
278
|
+
body: ({ args }) => `${RESOLVE_FEATURE_NAME}
|
|
279
|
+
|
|
280
|
+
## Instructions
|
|
226
281
|
|
|
227
282
|
1. Check whether \`osddt.plan.md\` already exists in the working directory:
|
|
228
283
|
- If it **does not exist**, proceed to generate it.
|
|
@@ -231,9 +286,16 @@ Run the following command to create the implementation plan:
|
|
|
231
286
|
- **Update** — read the existing file and apply targeted changes based on ${args}
|
|
232
287
|
- **Do nothing** — stop here and leave the file as-is
|
|
233
288
|
2. Read \`osddt.spec.md\` from the working directory
|
|
234
|
-
3.
|
|
235
|
-
|
|
236
|
-
|
|
289
|
+
3. Check for unanswered open questions in the spec:
|
|
290
|
+
- Count the items in the **Open Questions** section that have no corresponding entry in the **Decisions** section.
|
|
291
|
+
- If there are any unanswered questions, inform the user: "This spec has X unanswered open question(s)."
|
|
292
|
+
- Ask the user whether to:
|
|
293
|
+
- **Clarify first** — stop here and suggest running \`/osddt.clarify <feature-name>\` instead
|
|
294
|
+
- **Proceed anyway** — continue with plan generation using the spec as-is
|
|
295
|
+
- If there are no unanswered questions, proceed silently.
|
|
296
|
+
4. Break down the implementation into logical phases and steps
|
|
297
|
+
5. Identify technical decisions, dependencies, and risks
|
|
298
|
+
6. Write the plan to \`osddt.plan.md\` in the working directory
|
|
237
299
|
|
|
238
300
|
## Plan Format
|
|
239
301
|
|
|
@@ -253,20 +315,21 @@ ${args}
|
|
|
253
315
|
Run the following command to generate the task list:
|
|
254
316
|
|
|
255
317
|
\`\`\`
|
|
256
|
-
/osddt.tasks
|
|
318
|
+
/osddt.tasks
|
|
257
319
|
\`\`\`
|
|
258
320
|
`,
|
|
259
321
|
},
|
|
260
322
|
{
|
|
261
323
|
name: 'osddt.tasks',
|
|
262
324
|
description: 'Generate actionable tasks from an implementation plan',
|
|
263
|
-
body: (
|
|
325
|
+
body: () => `${RESOLVE_FEATURE_NAME}
|
|
326
|
+
|
|
327
|
+
## Instructions
|
|
264
328
|
|
|
265
329
|
1. Check whether \`osddt.tasks.md\` already exists in the working directory:
|
|
266
330
|
- If it **does not exist**, proceed to generate it.
|
|
267
331
|
- If it **already exists**, ask the user whether to:
|
|
268
332
|
- **Regenerate** — discard the existing file and create a fresh task list from scratch
|
|
269
|
-
- **Update** — read the existing file and apply targeted changes based on ${args}
|
|
270
333
|
- **Do nothing** — stop here and leave the file as-is
|
|
271
334
|
2. Read \`osddt.plan.md\` from the working directory
|
|
272
335
|
3. Break each phase into discrete, executable tasks
|
|
@@ -281,26 +344,22 @@ The task list should include:
|
|
|
281
344
|
- **Dependencies**: Note which tasks must complete before others
|
|
282
345
|
- **Definition of Done**: Clear completion criteria per phase
|
|
283
346
|
|
|
284
|
-
## Arguments
|
|
285
|
-
|
|
286
|
-
${args}
|
|
287
|
-
|
|
288
347
|
## Next Step
|
|
289
348
|
|
|
290
349
|
Run the following command to start implementing tasks:
|
|
291
350
|
|
|
292
351
|
\`\`\`
|
|
293
|
-
/osddt.implement
|
|
352
|
+
/osddt.implement
|
|
294
353
|
\`\`\`
|
|
295
354
|
`,
|
|
296
355
|
},
|
|
297
356
|
{
|
|
298
357
|
name: 'osddt.implement',
|
|
299
358
|
description: 'Execute tasks from the task list one by one',
|
|
300
|
-
body: (
|
|
359
|
+
body: () => `## Instructions
|
|
301
360
|
|
|
302
361
|
1. Check whether \`osddt.tasks.md\` exists in the working directory:
|
|
303
|
-
- If it **does not exist**, stop and ask the user to run \`/osddt.tasks
|
|
362
|
+
- If it **does not exist**, stop and ask the user to run \`/osddt.tasks\` first.
|
|
304
363
|
2. Read \`osddt.tasks.md\` from the working directory
|
|
305
364
|
3. Find the next unchecked task (\`- [ ]\`)
|
|
306
365
|
4. Implement that task following the spec (\`osddt.spec.md\`) and plan (\`osddt.plan.md\`) in the working directory
|
|
@@ -314,44 +373,31 @@ Run the following command to start implementing tasks:
|
|
|
314
373
|
- Write tests for new functionality when applicable
|
|
315
374
|
- Ask for clarification if requirements are ambiguous
|
|
316
375
|
|
|
317
|
-
## Arguments
|
|
318
|
-
|
|
319
|
-
${args}
|
|
320
|
-
|
|
321
376
|
## Next Step
|
|
322
377
|
|
|
323
378
|
Once all tasks are checked off, run the following command to mark the feature as done:
|
|
324
379
|
|
|
325
380
|
\`\`\`
|
|
326
|
-
/osddt.done
|
|
381
|
+
/osddt.done
|
|
327
382
|
\`\`\`
|
|
328
383
|
`,
|
|
329
384
|
},
|
|
330
385
|
{
|
|
331
386
|
name: 'osddt.done',
|
|
332
387
|
description: 'Mark a feature as done and move it from working-on to done',
|
|
333
|
-
body: (
|
|
388
|
+
body: ({ npxCommand }) => `## Instructions
|
|
334
389
|
|
|
335
|
-
1.
|
|
336
|
-
|
|
337
|
-
- If \`repoType\` is \`"single"\`: the project path is the repository root.
|
|
338
|
-
- If \`repoType\` is \`"monorepo"\`: ask the user which package to work on (e.g. \`packages/my-package\`), then use \`<repo-root>/<package>\` as the project path.
|
|
339
|
-
2. Derive the feature name from ${args} using the same rules as the other commands (last segment of a branch name, or a kebab-cased slug — subject to the 30-character limit). This must match the folder name under \`working-on/\`.
|
|
340
|
-
3. Confirm all tasks in \`osddt.tasks.md\` are checked off (\`- [x]\`)
|
|
341
|
-
4. Run the following command to move the feature folder from \`working-on\` to \`done\`:
|
|
390
|
+
1. Confirm all tasks in \`osddt.tasks.md\` are checked off (\`- [x]\`)
|
|
391
|
+
2. Run the following command to move the feature folder from \`working-on\` to \`done\`:
|
|
342
392
|
|
|
343
393
|
\`\`\`
|
|
344
|
-
|
|
394
|
+
${npxCommand} done <feature-name> --dir <project-path>
|
|
345
395
|
\`\`\`
|
|
346
396
|
|
|
347
397
|
The command will automatically prefix the destination folder name with today's date in \`YYYY-MM-DD\` format.
|
|
348
398
|
For example, \`working-on/feature-a\` will be moved to \`done/YYYY-MM-DD-feature-a\`.
|
|
349
399
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
## Arguments
|
|
353
|
-
|
|
354
|
-
${args}
|
|
400
|
+
3. Report the result of the command, including the full destination path
|
|
355
401
|
`,
|
|
356
402
|
},
|
|
357
403
|
];
|
|
@@ -364,11 +410,11 @@ description: "${description}"
|
|
|
364
410
|
|
|
365
411
|
${body}`;
|
|
366
412
|
}
|
|
367
|
-
function getClaudeTemplates(cwd) {
|
|
413
|
+
function getClaudeTemplates(cwd, npxCommand) {
|
|
368
414
|
const dir = path.join(cwd, CLAUDE_COMMANDS_DIR);
|
|
369
415
|
return COMMAND_DEFINITIONS.map((cmd) => ({
|
|
370
416
|
filePath: path.join(dir, `${cmd.name}.md`),
|
|
371
|
-
content: formatClaudeCommand(cmd.description, cmd.body('$ARGUMENTS')),
|
|
417
|
+
content: formatClaudeCommand(cmd.description, cmd.body({ args: '$ARGUMENTS', npxCommand })),
|
|
372
418
|
}));
|
|
373
419
|
}
|
|
374
420
|
|
|
@@ -380,11 +426,11 @@ prompt = """
|
|
|
380
426
|
${body}"""
|
|
381
427
|
`;
|
|
382
428
|
}
|
|
383
|
-
function getGeminiTemplates(cwd) {
|
|
429
|
+
function getGeminiTemplates(cwd, npxCommand) {
|
|
384
430
|
const dir = path.join(cwd, GEMINI_COMMANDS_DIR);
|
|
385
431
|
return COMMAND_DEFINITIONS.map((cmd) => ({
|
|
386
432
|
filePath: path.join(dir, `${cmd.name}.toml`),
|
|
387
|
-
content: formatGeminiCommand(cmd.description, cmd.body('{{args}}')),
|
|
433
|
+
content: formatGeminiCommand(cmd.description, cmd.body({ args: '{{args}}', npxCommand })),
|
|
388
434
|
}));
|
|
389
435
|
}
|
|
390
436
|
|
|
@@ -429,6 +475,21 @@ async function askAgents() {
|
|
|
429
475
|
});
|
|
430
476
|
}
|
|
431
477
|
|
|
478
|
+
const CANONICAL_PACKAGE_NAME = '@dezkareid/osddt';
|
|
479
|
+
const NPX_COMMAND = 'npx osddt';
|
|
480
|
+
const NPX_COMMAND_FALLBACK = `npx ${CANONICAL_PACKAGE_NAME}`;
|
|
481
|
+
async function resolveNpxCommand(cwd) {
|
|
482
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
483
|
+
try {
|
|
484
|
+
const pkg = await fs.readJson(pkgPath);
|
|
485
|
+
if (pkg.name === CANONICAL_PACKAGE_NAME)
|
|
486
|
+
return NPX_COMMAND;
|
|
487
|
+
}
|
|
488
|
+
catch {
|
|
489
|
+
// no package.json or unreadable — fall through to default
|
|
490
|
+
}
|
|
491
|
+
return NPX_COMMAND_FALLBACK;
|
|
492
|
+
}
|
|
432
493
|
const VALID_AGENTS = ['claude', 'gemini'];
|
|
433
494
|
const VALID_REPO_TYPES = ['single', 'monorepo'];
|
|
434
495
|
function parseAgents(raw) {
|
|
@@ -468,9 +529,10 @@ async function runSetup(cwd, rawAgents, rawRepoType) {
|
|
|
468
529
|
const repoType = rawRepoType !== undefined ? parseRepoType(rawRepoType) : await askRepoType();
|
|
469
530
|
if (rawRepoType === undefined)
|
|
470
531
|
console.log('');
|
|
532
|
+
const npxCommand = await resolveNpxCommand(cwd);
|
|
471
533
|
console.log('Setting up OSDDT command files...\n');
|
|
472
534
|
if (agents.includes('claude')) {
|
|
473
|
-
const claudeFiles = getClaudeTemplates(cwd);
|
|
535
|
+
const claudeFiles = getClaudeTemplates(cwd, npxCommand);
|
|
474
536
|
console.log('Claude Code commands (.claude/commands/):');
|
|
475
537
|
for (const file of claudeFiles) {
|
|
476
538
|
await writeCommandFile(file);
|
|
@@ -478,7 +540,7 @@ async function runSetup(cwd, rawAgents, rawRepoType) {
|
|
|
478
540
|
console.log('');
|
|
479
541
|
}
|
|
480
542
|
if (agents.includes('gemini')) {
|
|
481
|
-
const geminiFiles = getGeminiTemplates(cwd);
|
|
543
|
+
const geminiFiles = getGeminiTemplates(cwd, npxCommand);
|
|
482
544
|
console.log('Gemini CLI commands (.gemini/commands/):');
|
|
483
545
|
for (const file of geminiFiles) {
|
|
484
546
|
await writeCommandFile(file);
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
export declare
|
|
1
|
+
export declare function getRepoPreamble(npxCommand: string): string;
|
|
2
2
|
export declare const FEATURE_NAME_RULES = "### Feature Name Constraints\n\nWhen deriving a feature name from a description:\n\n- Use only lowercase letters, digits, and hyphens (`a-z`, `0-9`, `-`)\n- Replace spaces and special characters with hyphens\n- Remove consecutive hyphens (e.g. `--` \u2192 `-`)\n- Remove leading and trailing hyphens\n- **Maximum length: 30 characters** \u2014 if the derived name exceeds 30 characters, truncate at the last hyphen boundary before or at the 30th character\n- If the input is already a valid branch name (no spaces, kebab-case or slash-separated), apply the 30-character limit to the last segment only (after the last `/`)\n- Reject (and ask the user to provide a shorter name) if no valid name can be derived after truncation\n\n**Examples:**\n\n| Input | Derived feature name |\n| ----------------------------------------------------- | ---------------------------- |\n| `Add user authentication` | `add-user-authentication` |\n| `Implement real-time notifications for dashboard` | `implement-real-time` |\n| `feat/add-user-authentication` | `add-user-authentication` |\n| `feat/implement-real-time-notifications-for-dashboard` | `implement-real-time` |\n";
|
|
3
3
|
export declare const WORKING_DIR_STEP = "Check whether the working directory `<project-path>/working-on/<feature-name>` already exists:\n - If it **does not exist**, create it:\n ```\n mkdir -p <project-path>/working-on/<feature-name>\n ```\n - If it **already exists**, warn the user and ask whether to:\n - **Resume** \u2014 continue into the existing folder (proceed to the next step without recreating it)\n - **Abort** \u2014 stop and do nothing";
|
|
4
|
+
export declare const RESOLVE_FEATURE_NAME = "### Resolving the Feature Name\n\nUse the following logic to determine `<feature-name>`:\n\n1. If arguments were provided, derive the feature name from them:\n - If the argument looks like a branch name (no spaces, kebab-case or slash-separated), use the last segment (after the last `/`, or the full value if no `/` is present).\n - Otherwise treat it as a human-readable description and convert it to a feature name following the constraints in the Feature Name Constraints section.\n2. If **no arguments were provided**:\n - List all folders under `<project-path>/working-on/`.\n - If there is **only one folder**, use it automatically and inform the user.\n - If there are **multiple folders**, present the list to the user and ask them to pick one.\n - If there are **no folders**, inform the user that no in-progress features were found and stop.";
|
|
4
5
|
export type ArgPlaceholder = '$ARGUMENTS' | '{{args}}';
|
|
6
|
+
export interface CommandDefinitionContext {
|
|
7
|
+
args: ArgPlaceholder;
|
|
8
|
+
npxCommand: string;
|
|
9
|
+
}
|
|
5
10
|
export interface CommandDefinition {
|
|
6
11
|
name: string;
|
|
7
12
|
description: string;
|
|
8
|
-
body: (
|
|
13
|
+
body: (ctx: CommandDefinitionContext) => string;
|
|
9
14
|
}
|
|
10
15
|
export declare const COMMAND_DEFINITIONS: CommandDefinition[];
|