@dezkareid/osddt 0.0.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -1
- package/dist/commands/done.d.ts +2 -0
- package/dist/commands/done.spec.d.ts +1 -0
- package/dist/commands/meta-info.d.ts +2 -0
- package/dist/commands/meta-info.spec.d.ts +1 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +501 -0
- package/dist/templates/claude.d.ts +6 -0
- package/dist/templates/claude.spec.d.ts +1 -0
- package/dist/templates/gemini.d.ts +6 -0
- package/dist/templates/gemini.spec.d.ts +1 -0
- package/dist/templates/shared.d.ts +10 -0
- package/dist/templates/shared.spec.d.ts +1 -0
- package/dist/utils/prompt.d.ts +2 -0
- package/package.json +52 -6
- package/AGENTS.md +0 -32
package/README.md
CHANGED
|
@@ -1,2 +1,40 @@
|
|
|
1
1
|
# osddt
|
|
2
|
-
Other spec
|
|
2
|
+
Other spec driven development tool but for monorepo
|
|
3
|
+
|
|
4
|
+
## CLI Commands
|
|
5
|
+
|
|
6
|
+
| Command | Description |
|
|
7
|
+
| ------------------------------ | ------------------------------------------------------------- |
|
|
8
|
+
| `osddt setup` | Generate agent command files for Claude and Gemini |
|
|
9
|
+
| `osddt meta-info` | Output current branch and date as JSON |
|
|
10
|
+
| `osddt done <feature-name>` | Move `working-on/<feature>` to `done/<feature>` |
|
|
11
|
+
|
|
12
|
+
## Command Templates
|
|
13
|
+
|
|
14
|
+
Run `osddt setup` once to generate the agent command files.
|
|
15
|
+
|
|
16
|
+
`osddt.research` and `osddt.start` are **peer entry points** — use whichever fits your situation. Both lead to `osddt.spec`. Use `osddt.continue` to resume an in-progress feature from a new session.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
osddt.continue ──────────────────────────────────────────────────────────────────────────────┐
|
|
20
|
+
│
|
|
21
|
+
osddt.research ──┐ │
|
|
22
|
+
├──► osddt.spec → osddt.plan → osddt.tasks → osddt.implement → osddt.done ◄─┘
|
|
23
|
+
osddt.start ──┘
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
| Template | Description |
|
|
27
|
+
| ------------------ | ------------------------------------------------------------------ |
|
|
28
|
+
| `osddt.continue` | Detect the current workflow phase and prompt the next command |
|
|
29
|
+
| `osddt.research` | Research a topic and write a research file to inform the spec |
|
|
30
|
+
| `osddt.start` | Start a new feature by creating a branch and working-on folder |
|
|
31
|
+
| `osddt.spec` | Analyze requirements and write a feature specification |
|
|
32
|
+
| `osddt.plan` | Create a technical implementation plan from a specification |
|
|
33
|
+
| `osddt.tasks` | Generate actionable tasks from an implementation plan |
|
|
34
|
+
| `osddt.implement` | Execute tasks from the task list one by one |
|
|
35
|
+
| `osddt.done` | Mark a feature as done and move it from working-on to done |
|
|
36
|
+
|
|
37
|
+
Generated files are placed in:
|
|
38
|
+
|
|
39
|
+
- `.claude/commands/osddt.<name>.md` (Claude Code)
|
|
40
|
+
- `.gemini/commands/osddt.<name>.toml` (Gemini CLI)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import select from '@inquirer/select';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
|
|
8
|
+
const REPO_PREAMBLE = `## Context
|
|
9
|
+
|
|
10
|
+
Before proceeding, run the following command and parse the JSON output to get the current branch and date:
|
|
11
|
+
|
|
12
|
+
\`\`\`
|
|
13
|
+
npx osddt meta-info
|
|
14
|
+
\`\`\`
|
|
15
|
+
|
|
16
|
+
## Repository Configuration
|
|
17
|
+
|
|
18
|
+
Before proceeding, read the \`.osddtrc\` file in the root of the repository to determine the project path.
|
|
19
|
+
|
|
20
|
+
\`\`\`json
|
|
21
|
+
// .osddtrc example
|
|
22
|
+
{ "repoType": "monorepo" | "single" }
|
|
23
|
+
\`\`\`
|
|
24
|
+
|
|
25
|
+
- If \`repoType\` is \`"single"\`: the project path is the repository root.
|
|
26
|
+
- 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.
|
|
27
|
+
|
|
28
|
+
## Working Directory
|
|
29
|
+
|
|
30
|
+
All generated files live under \`<project-path>/working-on/<feature-name>/\`. The \`<feature-name>\` is derived from the arguments provided. Create the directory if it does not exist.
|
|
31
|
+
|
|
32
|
+
> All file paths in the instructions below are relative to \`<project-path>/working-on/<feature-name>/\`.
|
|
33
|
+
|
|
34
|
+
`;
|
|
35
|
+
const FEATURE_NAME_RULES = `### Feature Name Constraints
|
|
36
|
+
|
|
37
|
+
When deriving a feature name from a description:
|
|
38
|
+
|
|
39
|
+
- Use only lowercase letters, digits, and hyphens (\`a-z\`, \`0-9\`, \`-\`)
|
|
40
|
+
- Replace spaces and special characters with hyphens
|
|
41
|
+
- Remove consecutive hyphens (e.g. \`--\` → \`-\`)
|
|
42
|
+
- Remove leading and trailing hyphens
|
|
43
|
+
- **Maximum length: 30 characters** — if the derived name exceeds 30 characters, truncate at the last hyphen boundary before or at the 30th character
|
|
44
|
+
- 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 \`/\`)
|
|
45
|
+
- Reject (and ask the user to provide a shorter name) if no valid name can be derived after truncation
|
|
46
|
+
|
|
47
|
+
**Examples:**
|
|
48
|
+
|
|
49
|
+
| Input | Derived feature name |
|
|
50
|
+
| ----------------------------------------------------- | ---------------------------- |
|
|
51
|
+
| \`Add user authentication\` | \`add-user-authentication\` |
|
|
52
|
+
| \`Implement real-time notifications for dashboard\` | \`implement-real-time\` |
|
|
53
|
+
| \`feat/add-user-authentication\` | \`add-user-authentication\` |
|
|
54
|
+
| \`feat/implement-real-time-notifications-for-dashboard\` | \`implement-real-time\` |
|
|
55
|
+
`;
|
|
56
|
+
const WORKING_DIR_STEP = `Check whether the working directory \`<project-path>/working-on/<feature-name>\` already exists:
|
|
57
|
+
- If it **does not exist**, create it:
|
|
58
|
+
\`\`\`
|
|
59
|
+
mkdir -p <project-path>/working-on/<feature-name>
|
|
60
|
+
\`\`\`
|
|
61
|
+
- If it **already exists**, warn the user and ask whether to:
|
|
62
|
+
- **Resume** — continue into the existing folder (proceed to the next step without recreating it)
|
|
63
|
+
- **Abort** — stop and do nothing`;
|
|
64
|
+
const COMMAND_DEFINITIONS = [
|
|
65
|
+
{
|
|
66
|
+
name: 'osddt.continue',
|
|
67
|
+
description: 'Detect the current workflow phase and prompt the next command to run',
|
|
68
|
+
body: (args) => `${REPO_PREAMBLE}## Instructions
|
|
69
|
+
|
|
70
|
+
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:
|
|
71
|
+
|
|
72
|
+
| Condition | Current phase | Run next |
|
|
73
|
+
| --------- | ------------- | -------- |
|
|
74
|
+
| \`osddt.tasks.md\` exists **and** has at least one unchecked task (\`- [ ]\`) | Implementing | \`/osddt.implement ${args}\` |
|
|
75
|
+
| \`osddt.tasks.md\` exists **and** all tasks are checked (\`- [x]\`) | Ready to close | \`/osddt.done ${args}\` |
|
|
76
|
+
| \`osddt.plan.md\` exists | Planning done | \`/osddt.tasks ${args}\` |
|
|
77
|
+
| \`osddt.spec.md\` exists | Spec done | \`/osddt.plan ${args}\` |
|
|
78
|
+
| \`osddt.research.md\` exists | Research done | \`/osddt.spec ${args}\` |
|
|
79
|
+
| None of the above | Not started | \`/osddt.spec ${args}\` (or \`/osddt.research ${args}\` if research is needed first) |
|
|
80
|
+
|
|
81
|
+
Report which file was found, which phase that corresponds to, and the exact command the user should run next.
|
|
82
|
+
|
|
83
|
+
## Arguments
|
|
84
|
+
|
|
85
|
+
${args}
|
|
86
|
+
`,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'osddt.research',
|
|
90
|
+
description: 'Research a topic and write a research file to inform the feature specification',
|
|
91
|
+
body: (args) => `${REPO_PREAMBLE}## Instructions
|
|
92
|
+
|
|
93
|
+
The argument provided is: ${args}
|
|
94
|
+
|
|
95
|
+
Determine the feature name using the following logic:
|
|
96
|
+
|
|
97
|
+
1. If ${args} looks like a branch name (e.g. \`feat/my-feature\`, \`my-feature-branch\` — no spaces, kebab-case or slash-separated), derive the feature name from the last segment (after the last \`/\`, or the full value if no \`/\` is present).
|
|
98
|
+
2. Otherwise treat ${args} as a human-readable topic description and convert it to a feature name.
|
|
99
|
+
|
|
100
|
+
Apply the constraints below before using the name:
|
|
101
|
+
|
|
102
|
+
${FEATURE_NAME_RULES}
|
|
103
|
+
|
|
104
|
+
Once the feature name is determined:
|
|
105
|
+
|
|
106
|
+
3. ${WORKING_DIR_STEP}
|
|
107
|
+
|
|
108
|
+
4. Research the topic thoroughly:
|
|
109
|
+
- Explore the existing codebase for relevant patterns, conventions, and prior art
|
|
110
|
+
- Identify related files, modules, and dependencies
|
|
111
|
+
- Note any constraints, risks, or open questions
|
|
112
|
+
|
|
113
|
+
5. Write the findings to \`osddt.research.md\` in the working directory using the following format:
|
|
114
|
+
|
|
115
|
+
## Research Format
|
|
116
|
+
|
|
117
|
+
The research file should include:
|
|
118
|
+
- **Topic**: What was researched and why
|
|
119
|
+
- **Codebase Findings**: Relevant existing code, patterns, and conventions found
|
|
120
|
+
- **External References**: Libraries, APIs, or documentation consulted
|
|
121
|
+
- **Key Insights**: Important discoveries that should inform the specification
|
|
122
|
+
- **Constraints & Risks**: Known limitations or risks uncovered during research
|
|
123
|
+
- **Open Questions**: Ambiguities that the specification phase should resolve
|
|
124
|
+
|
|
125
|
+
## Arguments
|
|
126
|
+
|
|
127
|
+
${args}
|
|
128
|
+
|
|
129
|
+
## Next Step
|
|
130
|
+
|
|
131
|
+
Run the following command to write the feature specification:
|
|
132
|
+
|
|
133
|
+
\`\`\`
|
|
134
|
+
/osddt.spec ${args}
|
|
135
|
+
\`\`\`
|
|
136
|
+
`,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'osddt.start',
|
|
140
|
+
description: 'Start a new feature by creating a branch and working-on folder',
|
|
141
|
+
body: (args) => `${REPO_PREAMBLE}## Instructions
|
|
142
|
+
|
|
143
|
+
The argument provided is: ${args}
|
|
144
|
+
|
|
145
|
+
Determine the branch name using the following logic:
|
|
146
|
+
|
|
147
|
+
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.
|
|
148
|
+
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.
|
|
149
|
+
|
|
150
|
+
Apply the constraints below to the feature name (the segment after the last \`/\`) before using it:
|
|
151
|
+
|
|
152
|
+
${FEATURE_NAME_RULES}
|
|
153
|
+
|
|
154
|
+
Once the branch name is determined:
|
|
155
|
+
|
|
156
|
+
3. Check whether the branch already exists locally or remotely:
|
|
157
|
+
- If it **does not exist**, create and switch to it:
|
|
158
|
+
\`\`\`
|
|
159
|
+
git checkout -b <branch-name>
|
|
160
|
+
\`\`\`
|
|
161
|
+
- If it **already exists**, warn the user and ask whether to:
|
|
162
|
+
- **Resume** — switch to the existing branch (\`git checkout <branch-name>\`) and continue
|
|
163
|
+
- **Abort** — stop and do nothing
|
|
164
|
+
|
|
165
|
+
4. ${WORKING_DIR_STEP}
|
|
166
|
+
|
|
167
|
+
Where \`<feature-name>\` is the last segment of the branch name (after the last \`/\`, or the full branch name if no \`/\` is present).
|
|
168
|
+
|
|
169
|
+
5. Report the branch name and working directory that were created or resumed.
|
|
170
|
+
|
|
171
|
+
## Arguments
|
|
172
|
+
|
|
173
|
+
${args}
|
|
174
|
+
|
|
175
|
+
## Next Step
|
|
176
|
+
|
|
177
|
+
Run the following command to write the feature specification:
|
|
178
|
+
|
|
179
|
+
\`\`\`
|
|
180
|
+
/osddt.spec ${args}
|
|
181
|
+
\`\`\`
|
|
182
|
+
`,
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: 'osddt.spec',
|
|
186
|
+
description: 'Analyze requirements and write a feature specification',
|
|
187
|
+
body: (args) => `## Instructions
|
|
188
|
+
|
|
189
|
+
1. Check whether \`osddt.research.md\` exists in the working directory.
|
|
190
|
+
- If it exists, read it and use its findings (key insights, constraints, open questions, codebase findings) as additional context when writing the specification.
|
|
191
|
+
- If it does not exist, proceed using only the requirements provided in ${args}.
|
|
192
|
+
2. Analyze the requirements provided in ${args}
|
|
193
|
+
3. Identify the core problem being solved
|
|
194
|
+
4. Define the scope, constraints, and acceptance criteria
|
|
195
|
+
5. Write the specification to \`osddt.spec.md\` in the working directory
|
|
196
|
+
|
|
197
|
+
## Specification Format
|
|
198
|
+
|
|
199
|
+
The spec should include:
|
|
200
|
+
- **Overview**: What and why
|
|
201
|
+
- **Requirements**: Functional and non-functional
|
|
202
|
+
- **Scope**: What is in and out of scope
|
|
203
|
+
- **Acceptance Criteria**: Clear, testable criteria
|
|
204
|
+
- **Open Questions**: Any ambiguities to resolve
|
|
205
|
+
|
|
206
|
+
> If \`osddt.research.md\` was found, add a **Research Summary** section that briefly references the key insights and constraints it identified.
|
|
207
|
+
|
|
208
|
+
## Arguments
|
|
209
|
+
|
|
210
|
+
${args}
|
|
211
|
+
|
|
212
|
+
## Next Step
|
|
213
|
+
|
|
214
|
+
Run the following command to create the implementation plan:
|
|
215
|
+
|
|
216
|
+
\`\`\`
|
|
217
|
+
/osddt.plan ${args}
|
|
218
|
+
\`\`\`
|
|
219
|
+
`,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: 'osddt.plan',
|
|
223
|
+
description: 'Create a technical implementation plan from a specification',
|
|
224
|
+
body: (args) => `## Instructions
|
|
225
|
+
|
|
226
|
+
1. Read \`osddt.spec.md\` from the working directory
|
|
227
|
+
2. Break down the implementation into logical phases and steps
|
|
228
|
+
3. Identify technical decisions, dependencies, and risks
|
|
229
|
+
4. Write the plan to \`osddt.plan.md\` in the working directory
|
|
230
|
+
|
|
231
|
+
## Plan Format
|
|
232
|
+
|
|
233
|
+
The plan should include:
|
|
234
|
+
- **Architecture Overview**: High-level design decisions
|
|
235
|
+
- **Implementation Phases**: Ordered phases with goals
|
|
236
|
+
- **Technical Dependencies**: Libraries, APIs, services needed
|
|
237
|
+
- **Risks & Mitigations**: Known risks and how to address them
|
|
238
|
+
- **Out of Scope**: Explicitly what will not be built
|
|
239
|
+
|
|
240
|
+
## Arguments
|
|
241
|
+
|
|
242
|
+
${args}
|
|
243
|
+
|
|
244
|
+
## Next Step
|
|
245
|
+
|
|
246
|
+
Run the following command to generate the task list:
|
|
247
|
+
|
|
248
|
+
\`\`\`
|
|
249
|
+
/osddt.tasks ${args}
|
|
250
|
+
\`\`\`
|
|
251
|
+
`,
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: 'osddt.tasks',
|
|
255
|
+
description: 'Generate actionable tasks from an implementation plan',
|
|
256
|
+
body: (args) => `## Instructions
|
|
257
|
+
|
|
258
|
+
1. Read \`osddt.plan.md\` from the working directory
|
|
259
|
+
2. Break each phase into discrete, executable tasks
|
|
260
|
+
3. Estimate complexity (S/M/L) for each task
|
|
261
|
+
4. Write the task list to \`osddt.tasks.md\` in the working directory
|
|
262
|
+
|
|
263
|
+
## Tasks Format
|
|
264
|
+
|
|
265
|
+
The task list should include:
|
|
266
|
+
- **Checklist** of tasks grouped by phase
|
|
267
|
+
- Each task should be: \`- [ ] [S/M/L] Description of task\`
|
|
268
|
+
- **Dependencies**: Note which tasks must complete before others
|
|
269
|
+
- **Definition of Done**: Clear completion criteria per phase
|
|
270
|
+
|
|
271
|
+
## Arguments
|
|
272
|
+
|
|
273
|
+
${args}
|
|
274
|
+
|
|
275
|
+
## Next Step
|
|
276
|
+
|
|
277
|
+
Run the following command to start implementing tasks:
|
|
278
|
+
|
|
279
|
+
\`\`\`
|
|
280
|
+
/osddt.implement ${args}
|
|
281
|
+
\`\`\`
|
|
282
|
+
`,
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: 'osddt.implement',
|
|
286
|
+
description: 'Execute tasks from the task list one by one',
|
|
287
|
+
body: (args) => `## Instructions
|
|
288
|
+
|
|
289
|
+
1. Read \`osddt.tasks.md\` from the working directory
|
|
290
|
+
2. Find the next unchecked task (\`- [ ]\`)
|
|
291
|
+
3. Implement that task following the spec (\`osddt.spec.md\`) and plan (\`osddt.plan.md\`) in the working directory
|
|
292
|
+
4. Mark the task as complete (\`- [x]\`) in \`osddt.tasks.md\`
|
|
293
|
+
5. Report what was done and any issues encountered
|
|
294
|
+
|
|
295
|
+
## Guidelines
|
|
296
|
+
|
|
297
|
+
- Implement one task at a time
|
|
298
|
+
- Follow the existing code style and conventions
|
|
299
|
+
- Write tests for new functionality when applicable
|
|
300
|
+
- Ask for clarification if requirements are ambiguous
|
|
301
|
+
|
|
302
|
+
## Arguments
|
|
303
|
+
|
|
304
|
+
${args}
|
|
305
|
+
|
|
306
|
+
## Next Step
|
|
307
|
+
|
|
308
|
+
Once all tasks are checked off, run the following command to mark the feature as done:
|
|
309
|
+
|
|
310
|
+
\`\`\`
|
|
311
|
+
/osddt.done ${args}
|
|
312
|
+
\`\`\`
|
|
313
|
+
`,
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: 'osddt.done',
|
|
317
|
+
description: 'Mark a feature as done and move it from working-on to done',
|
|
318
|
+
body: (args) => `## Instructions
|
|
319
|
+
|
|
320
|
+
1. Confirm all tasks in \`osddt.tasks.md\` are checked off (\`- [x]\`)
|
|
321
|
+
2. Run the following command to move the feature folder from \`working-on\` to \`done\`:
|
|
322
|
+
|
|
323
|
+
\`\`\`
|
|
324
|
+
npx osddt done ${args}
|
|
325
|
+
\`\`\`
|
|
326
|
+
|
|
327
|
+
The command will automatically prefix the destination folder name with today's date in \`YYYY-MM-DD\` format.
|
|
328
|
+
For example, \`working-on/feature-a\` will be moved to \`done/YYYY-MM-DD-feature-a\`.
|
|
329
|
+
|
|
330
|
+
3. Report the result of the command, including the full destination path
|
|
331
|
+
|
|
332
|
+
## Arguments
|
|
333
|
+
|
|
334
|
+
${args}
|
|
335
|
+
`,
|
|
336
|
+
},
|
|
337
|
+
];
|
|
338
|
+
|
|
339
|
+
const CLAUDE_COMMANDS_DIR = '.claude/commands';
|
|
340
|
+
function formatClaudeCommand(description, body) {
|
|
341
|
+
return `---
|
|
342
|
+
description: "${description}"
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
${body}`;
|
|
346
|
+
}
|
|
347
|
+
function getClaudeTemplates(cwd) {
|
|
348
|
+
const dir = path.join(cwd, CLAUDE_COMMANDS_DIR);
|
|
349
|
+
return COMMAND_DEFINITIONS.map((cmd) => ({
|
|
350
|
+
filePath: path.join(dir, `${cmd.name}.md`),
|
|
351
|
+
content: formatClaudeCommand(cmd.description, cmd.body('$ARGUMENTS')),
|
|
352
|
+
}));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const GEMINI_COMMANDS_DIR = '.gemini/commands';
|
|
356
|
+
function formatGeminiCommand(description, body) {
|
|
357
|
+
return `description = "${description}"
|
|
358
|
+
|
|
359
|
+
prompt = """
|
|
360
|
+
${body}"""
|
|
361
|
+
`;
|
|
362
|
+
}
|
|
363
|
+
function getGeminiTemplates(cwd) {
|
|
364
|
+
const dir = path.join(cwd, GEMINI_COMMANDS_DIR);
|
|
365
|
+
return COMMAND_DEFINITIONS.map((cmd) => ({
|
|
366
|
+
filePath: path.join(dir, `${cmd.name}.toml`),
|
|
367
|
+
content: formatGeminiCommand(cmd.description, cmd.body('{{args}}')),
|
|
368
|
+
}));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async function askRepoType() {
|
|
372
|
+
return select({
|
|
373
|
+
message: 'What type of repository is this?',
|
|
374
|
+
choices: [
|
|
375
|
+
{
|
|
376
|
+
name: 'Single repo',
|
|
377
|
+
value: 'single',
|
|
378
|
+
description: 'A standalone project with one package',
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: 'Monorepo',
|
|
382
|
+
value: 'monorepo',
|
|
383
|
+
description: 'A repository containing multiple packages',
|
|
384
|
+
},
|
|
385
|
+
],
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async function writeCommandFile(file) {
|
|
390
|
+
await fs.ensureDir(path.dirname(file.filePath));
|
|
391
|
+
await fs.writeFile(file.filePath, file.content, 'utf-8');
|
|
392
|
+
console.log(` Created: ${file.filePath}`);
|
|
393
|
+
}
|
|
394
|
+
async function writeConfig(cwd, config) {
|
|
395
|
+
const configPath = path.join(cwd, '.osddtrc');
|
|
396
|
+
await fs.writeJson(configPath, config, { spaces: 2 });
|
|
397
|
+
console.log(`\nSaved config: ${configPath}`);
|
|
398
|
+
}
|
|
399
|
+
async function runSetup(cwd) {
|
|
400
|
+
const repoType = await askRepoType();
|
|
401
|
+
console.log('');
|
|
402
|
+
console.log('Setting up OSDDT command files...\n');
|
|
403
|
+
const claudeFiles = getClaudeTemplates(cwd);
|
|
404
|
+
const geminiFiles = getGeminiTemplates(cwd);
|
|
405
|
+
console.log('Claude Code commands (.claude/commands/):');
|
|
406
|
+
for (const file of claudeFiles) {
|
|
407
|
+
await writeCommandFile(file);
|
|
408
|
+
}
|
|
409
|
+
console.log('\nGemini CLI commands (.gemini/commands/):');
|
|
410
|
+
for (const file of geminiFiles) {
|
|
411
|
+
await writeCommandFile(file);
|
|
412
|
+
}
|
|
413
|
+
await writeConfig(cwd, { repoType });
|
|
414
|
+
console.log('\nSetup complete!');
|
|
415
|
+
console.log('Commands created: osddt.spec, osddt.plan, osddt.tasks, osddt.implement');
|
|
416
|
+
}
|
|
417
|
+
function setupCommand() {
|
|
418
|
+
const cmd = new Command('setup');
|
|
419
|
+
cmd
|
|
420
|
+
.description('Create OSDDT command files for Claude and Gemini CLI agents')
|
|
421
|
+
.option('-d, --dir <directory>', 'target directory', process.cwd())
|
|
422
|
+
.action(async (options) => {
|
|
423
|
+
const targetDir = path.resolve(options.dir);
|
|
424
|
+
await runSetup(targetDir);
|
|
425
|
+
});
|
|
426
|
+
return cmd;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function getCurrentBranch() {
|
|
430
|
+
try {
|
|
431
|
+
return execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();
|
|
432
|
+
}
|
|
433
|
+
catch {
|
|
434
|
+
return 'unknown';
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
function getCurrentDate() {
|
|
438
|
+
return new Date().toISOString().split('T')[0];
|
|
439
|
+
}
|
|
440
|
+
function metaInfoCommand() {
|
|
441
|
+
const cmd = new Command('meta-info');
|
|
442
|
+
cmd
|
|
443
|
+
.description('Output project meta information as JSON (branch, date)')
|
|
444
|
+
.action(() => {
|
|
445
|
+
const info = {
|
|
446
|
+
branch: getCurrentBranch(),
|
|
447
|
+
date: getCurrentDate(),
|
|
448
|
+
};
|
|
449
|
+
console.log(JSON.stringify(info));
|
|
450
|
+
});
|
|
451
|
+
return cmd;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function todayPrefix() {
|
|
455
|
+
const now = new Date();
|
|
456
|
+
const yyyy = now.getFullYear();
|
|
457
|
+
const mm = String(now.getMonth() + 1).padStart(2, '0');
|
|
458
|
+
const dd = String(now.getDate()).padStart(2, '0');
|
|
459
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
460
|
+
}
|
|
461
|
+
async function runDone(featureName, cwd) {
|
|
462
|
+
const configPath = path.join(cwd, '.osddtrc');
|
|
463
|
+
if (!(await fs.pathExists(configPath))) {
|
|
464
|
+
console.error('Error: .osddtrc not found. Run `osddt setup` first.');
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
await fs.readJson(configPath);
|
|
468
|
+
const projectPath = cwd;
|
|
469
|
+
const src = path.join(projectPath, 'working-on', featureName);
|
|
470
|
+
const destName = `${todayPrefix()}-${featureName}`;
|
|
471
|
+
const dest = path.join(projectPath, 'done', destName);
|
|
472
|
+
if (!(await fs.pathExists(src))) {
|
|
473
|
+
console.error(`Error: working-on/${featureName} does not exist.`);
|
|
474
|
+
process.exit(1);
|
|
475
|
+
}
|
|
476
|
+
await fs.ensureDir(path.dirname(dest));
|
|
477
|
+
await fs.move(src, dest);
|
|
478
|
+
console.log(`Moved: working-on/${featureName} → done/${destName}`);
|
|
479
|
+
}
|
|
480
|
+
function doneCommand() {
|
|
481
|
+
const cmd = new Command('done');
|
|
482
|
+
cmd
|
|
483
|
+
.description('Move a feature from working-on/<feature-name> to done/<feature-name>')
|
|
484
|
+
.argument('<feature-name>', 'name of the feature to mark as done')
|
|
485
|
+
.option('-d, --dir <directory>', 'project directory', process.cwd())
|
|
486
|
+
.action(async (featureName, options) => {
|
|
487
|
+
const targetDir = path.resolve(options.dir);
|
|
488
|
+
await runDone(featureName, targetDir);
|
|
489
|
+
});
|
|
490
|
+
return cmd;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const program = new Command();
|
|
494
|
+
program
|
|
495
|
+
.name('osddt')
|
|
496
|
+
.description('Spec-Driven Development tooling for monorepos and single-package repos')
|
|
497
|
+
.version('0.0.0');
|
|
498
|
+
program.addCommand(setupCommand());
|
|
499
|
+
program.addCommand(metaInfoCommand());
|
|
500
|
+
program.addCommand(doneCommand());
|
|
501
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const REPO_PREAMBLE = "## Context\n\nBefore proceeding, run the following command and parse the JSON output to get the current branch and date:\n\n```\nnpx osddt meta-info\n```\n\n## Repository Configuration\n\nBefore proceeding, read the `.osddtrc` file in the root of the repository to determine the project path.\n\n```json\n// .osddtrc example\n{ \"repoType\": \"monorepo\" | \"single\" }\n```\n\n- If `repoType` is `\"single\"`: the project path is the repository root.\n- 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.\n\n## Working Directory\n\nAll generated files live under `<project-path>/working-on/<feature-name>/`. The `<feature-name>` is derived from the arguments provided. Create the directory if it does not exist.\n\n> All file paths in the instructions below are relative to `<project-path>/working-on/<feature-name>/`.\n\n";
|
|
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
|
+
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 type ArgPlaceholder = '$ARGUMENTS' | '{{args}}';
|
|
5
|
+
export interface CommandDefinition {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
body: (args: ArgPlaceholder) => string;
|
|
9
|
+
}
|
|
10
|
+
export declare const COMMAND_DEFINITIONS: CommandDefinition[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,10 +1,56 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dezkareid/osddt",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Package for Spec-Driven Development workflow",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
"spec-driven",
|
|
7
|
+
"cli"
|
|
8
|
+
],
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public",
|
|
15
|
+
"provenance": true
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/dezkareid/osddt.git"
|
|
20
|
+
},
|
|
21
|
+
"author": "Joel Humberto Gomez Paredes <elmaildeldezkareid@gmail.com>",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/dezkareid/osddt/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/dezkareid/osddt#readme",
|
|
27
|
+
"type": "module",
|
|
28
|
+
"bin": {
|
|
29
|
+
"osddt": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@inquirer/select": "5.0.7",
|
|
34
|
+
"commander": "12.0.0",
|
|
35
|
+
"fs-extra": "11.2.0",
|
|
36
|
+
"tslib": "2.8.1"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@rollup/plugin-typescript": "12.1.4",
|
|
40
|
+
"@types/fs-extra": "11.0.4",
|
|
41
|
+
"@types/node": "25.0.10",
|
|
42
|
+
"eslint": "9.39.2",
|
|
43
|
+
"prettier": "3.8.1",
|
|
44
|
+
"rollup": "4.56.0",
|
|
45
|
+
"typescript": "5.9.3",
|
|
46
|
+
"vitest": "4.0.18"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "rollup -c",
|
|
50
|
+
"dev": "rollup -c --watch",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"lint": "eslint src",
|
|
54
|
+
"format": "prettier --write src"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/AGENTS.md
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# Agent Instructions: dezkareid monorepo
|
|
2
|
-
|
|
3
|
-
This file provides critical context and dependency information for AI agents working in this project.
|
|
4
|
-
|
|
5
|
-
## Critical Dependency Versions
|
|
6
|
-
|
|
7
|
-
The following versions are established across the project's packages and should be respected when adding new dependencies or troubleshooting.
|
|
8
|
-
|
|
9
|
-
### Core Languages & Runtimes
|
|
10
|
-
- **TypeScript**: `5.9.3`
|
|
11
|
-
|
|
12
|
-
### Build & Bundling Tools
|
|
13
|
-
- **Rollup**: `4.56.0`
|
|
14
|
-
|
|
15
|
-
### Testing Frameworks
|
|
16
|
-
- **Vitest**: `4.0.18`
|
|
17
|
-
|
|
18
|
-
### Linting & Formatting
|
|
19
|
-
- **ESLint**: `9.39.2`
|
|
20
|
-
- **Prettier**: `3.8.1`
|
|
21
|
-
|
|
22
|
-
### Type Definitions
|
|
23
|
-
- **@types/node**: `25.0.10`
|
|
24
|
-
- **@types/fs-extra**: `11.0.4`
|
|
25
|
-
|
|
26
|
-
### Key Libraries
|
|
27
|
-
- **Commander**: `12.0.0` (for CLI tools)
|
|
28
|
-
- **fs-extra**: `11.2.0`
|
|
29
|
-
- **globby**: `14.0.1`
|
|
30
|
-
|
|
31
|
-
## Project Structure & Conventions
|
|
32
|
-
- **Package Manager**: `npm` is the required package manager.
|