@dezkareid/osddt 1.9.0 → 1.10.1

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.
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function contextCommand(): Command;
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
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';
@@ -99,6 +100,19 @@ function getNextStepToSpec(args) {
99
100
 
100
101
  > Add a short description of what you're building so the spec has the right starting point.`;
101
102
  }
103
+ function getCustomContextStep(npxCommand, commandName) {
104
+ return `## Custom Context
105
+
106
+ Run the following command and, if it returns content, use it as additional context before proceeding:
107
+
108
+ \`\`\`
109
+ ${npxCommand} context ${commandName}
110
+ \`\`\`
111
+
112
+ If the command returns no output, skip this section and continue.
113
+
114
+ `;
115
+ }
102
116
  const COMMAND_DEFINITIONS = [
103
117
  {
104
118
  name: 'osddt.continue',
@@ -122,7 +136,7 @@ Report which file was found, which phase that corresponds to, and the exact comm
122
136
 
123
137
  > **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.
124
138
 
125
- ## Arguments
139
+ ${getCustomContextStep(npxCommand, 'continue')}## Arguments
126
140
 
127
141
  ${args}
128
142
  `,
@@ -164,7 +178,7 @@ The research file should include:
164
178
  - **Constraints & Risks**: Known limitations or risks uncovered during research
165
179
  - **Open Questions**: Ambiguities that the specification phase should resolve
166
180
 
167
- ## Arguments
181
+ ${getCustomContextStep(npxCommand, 'research')}## Arguments
168
182
 
169
183
  ${args}
170
184
 
@@ -204,7 +218,7 @@ Where \`<feature-name>\` is the last segment of the branch name (after the last
204
218
 
205
219
  5. Report the branch name and working directory that were created or resumed.
206
220
 
207
- ## Arguments
221
+ ${getCustomContextStep(npxCommand, 'start')}## Arguments
208
222
 
209
223
  ${args}
210
224
 
@@ -214,7 +228,7 @@ ${getNextStepToSpec(args)}
214
228
  {
215
229
  name: 'osddt.spec',
216
230
  description: 'Analyze requirements and write a feature specification',
217
- body: ({ args }) => `## Instructions
231
+ body: ({ args, npxCommand }) => `## Instructions
218
232
 
219
233
  1. Gather requirements from all available sources — combine them when multiple sources are present:
220
234
  - **Arguments** (${args}): use as the primary description of the feature, if provided.
@@ -242,7 +256,7 @@ The spec should include:
242
256
  > If \`osddt.research.md\` was found, add a **Research Summary** section that briefly references the key insights and user-facing constraints it identified.
243
257
  > If additional context was gathered from the conversation session, add a **Session Context** section summarising any extra details, decisions, or constraints discussed beyond what was passed as arguments.
244
258
 
245
- ## Arguments
259
+ ${getCustomContextStep(npxCommand, 'spec')}## Arguments
246
260
 
247
261
  ${args}
248
262
 
@@ -258,7 +272,7 @@ Run the following command to create the implementation plan:
258
272
  {
259
273
  name: 'osddt.clarify',
260
274
  description: 'Resolve open questions in the spec and record decisions',
261
- body: () => `## Instructions
275
+ body: ({ npxCommand }) => `## Instructions
262
276
 
263
277
  1. Check whether \`osddt.spec.md\` exists in the working directory:
264
278
  - If it **does not exist**, inform the user that no spec was found and suggest running \`/osddt.spec <brief feature description>\` first. Stop here.
@@ -287,12 +301,13 @@ Run the following command to create the implementation plan:
287
301
  \`\`\`
288
302
 
289
303
  > Note: if \`osddt.plan.md\` already exists, the plan should be regenerated to incorporate the decisions.
290
- `,
304
+
305
+ ${getCustomContextStep(npxCommand, 'clarify')}`,
291
306
  },
292
307
  {
293
308
  name: 'osddt.plan',
294
309
  description: 'Create a technical implementation plan from a specification',
295
- body: ({ args }) => `${RESOLVE_FEATURE_NAME}
310
+ body: ({ args, npxCommand }) => `${RESOLVE_FEATURE_NAME}
296
311
 
297
312
  ## Instructions
298
313
 
@@ -323,7 +338,7 @@ The plan should include:
323
338
  - **Risks & Mitigations**: Known risks and how to address them
324
339
  - **Out of Scope**: Explicitly what will not be built
325
340
 
326
- ## Arguments
341
+ ${getCustomContextStep(npxCommand, 'plan')}## Arguments
327
342
 
328
343
  ${args}
329
344
 
@@ -339,7 +354,7 @@ Run the following command to generate the task list:
339
354
  {
340
355
  name: 'osddt.tasks',
341
356
  description: 'Generate actionable tasks from an implementation plan',
342
- body: () => `${RESOLVE_FEATURE_NAME}
357
+ body: ({ npxCommand }) => `${RESOLVE_FEATURE_NAME}
343
358
 
344
359
  ## Instructions
345
360
 
@@ -361,7 +376,7 @@ The task list should include:
361
376
  - **Dependencies**: Note which tasks must complete before others
362
377
  - **Definition of Done**: Clear completion criteria per phase
363
378
 
364
- ## Next Step
379
+ ${getCustomContextStep(npxCommand, 'tasks')}## Next Step
365
380
 
366
381
  Run the following command to start implementing tasks:
367
382
 
@@ -373,7 +388,7 @@ Run the following command to start implementing tasks:
373
388
  {
374
389
  name: 'osddt.implement',
375
390
  description: 'Execute tasks from the task list one by one',
376
- body: () => `## Instructions
391
+ body: ({ npxCommand }) => `## Instructions
377
392
 
378
393
  1. Check whether \`osddt.tasks.md\` exists in the working directory:
379
394
  - If it **does not exist**, stop and ask the user to run \`/osddt.tasks\` first.
@@ -390,7 +405,7 @@ Run the following command to start implementing tasks:
390
405
  - Write tests for new functionality when applicable
391
406
  - Ask for clarification if requirements are ambiguous
392
407
 
393
- ## Next Step
408
+ ${getCustomContextStep(npxCommand, 'implement')}## Next Step
394
409
 
395
410
  Once all tasks are checked off, run the following command to mark the feature as done:
396
411
 
@@ -476,7 +491,7 @@ Display the full contents of \`osddt.tasks.md\` to the user. Then prompt them to
476
491
 
477
492
  > You can optionally run \`/osddt.clarify\` before implementing to resolve any Open Questions recorded in the spec.
478
493
 
479
- ## Arguments
494
+ ${getCustomContextStep(npxCommand, 'fast')}## Arguments
480
495
 
481
496
  ${args}
482
497
  `,
@@ -497,7 +512,8 @@ ${npxCommand} done <feature-name> --dir <project-path>
497
512
  For example, \`working-on/feature-a\` will be moved to \`done/YYYY-MM-DD-feature-a\`.
498
513
 
499
514
  3. Report the result of the command, including the full destination path
500
- `,
515
+
516
+ ${getCustomContextStep(npxCommand, 'done')}`,
501
517
  },
502
518
  ];
503
519
 
@@ -794,13 +810,76 @@ function updateCommand() {
794
810
  return cmd;
795
811
  }
796
812
 
813
+ const CONTEXT_DIR = 'osddt-context';
814
+ function stubContent(name) {
815
+ return `<!-- osddt context: ${name}
816
+ The content of this file will be shown to the agent under the "## Custom Context" section
817
+ when the osddt.${name} command is invoked. Add your project-specific instructions below. -->
818
+ `;
819
+ }
820
+ async function runRead(name, cwd) {
821
+ const filePath = path.join(cwd, CONTEXT_DIR, `${name}.md`);
822
+ let content;
823
+ try {
824
+ content = await fs.readFile(filePath, 'utf-8');
825
+ }
826
+ catch (err) {
827
+ if (err.code === 'ENOENT') {
828
+ return;
829
+ }
830
+ throw err;
831
+ }
832
+ process.stdout.write(content);
833
+ }
834
+ async function runInit(cwd) {
835
+ const contextDir = path.join(cwd, CONTEXT_DIR);
836
+ await fs.ensureDir(contextDir);
837
+ for (const cmd of COMMAND_DEFINITIONS) {
838
+ const name = cmd.name.replace(/^osddt\./, '');
839
+ const filePath = path.join(contextDir, `${name}.md`);
840
+ if (await fs.pathExists(filePath)) {
841
+ console.log(` Skipped (exists): ${CONTEXT_DIR}/${name}.md`);
842
+ }
843
+ else {
844
+ await fs.writeFile(filePath, stubContent(name), 'utf-8');
845
+ console.log(` Created: ${CONTEXT_DIR}/${name}.md`);
846
+ }
847
+ }
848
+ }
849
+ function contextCommand() {
850
+ const cmd = new Command('context');
851
+ cmd
852
+ .description('Output a custom context file or scaffold context stubs')
853
+ .argument('[name]', 'command name to read context for (e.g. plan, spec)')
854
+ .option('-d, --dir <directory>', 'project directory', process.cwd())
855
+ .option('--init', 'scaffold osddt-context/ with stub files for all commands')
856
+ .action(async (name, options) => {
857
+ const targetDir = path.resolve(options.dir);
858
+ if (options.init) {
859
+ await runInit(targetDir);
860
+ }
861
+ else if (name) {
862
+ await runRead(name, targetDir);
863
+ }
864
+ else {
865
+ cmd.help();
866
+ }
867
+ });
868
+ return cmd;
869
+ }
870
+
871
+ const __filename$1 = fileURLToPath(import.meta.url);
872
+ const __dirname$1 = path.dirname(__filename$1);
873
+ const pkgPath = path.join(__dirname$1, '..', 'package.json');
874
+ const pkg = fs.readJsonSync(pkgPath);
797
875
  const program = new Command();
798
876
  program
799
877
  .name('osddt')
800
878
  .description('Spec-Driven Development tooling for monorepos and single-package repos')
801
- .version('0.0.0');
879
+ .version(pkg.version);
802
880
  program.addCommand(setupCommand());
803
881
  program.addCommand(metaInfoCommand());
804
882
  program.addCommand(doneCommand());
805
883
  program.addCommand(updateCommand());
884
+ program.addCommand(contextCommand());
806
885
  program.parse(process.argv);
@@ -3,6 +3,7 @@ export declare const FEATURE_NAME_RULES = "### Feature Name Constraints\n\nWhen
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
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.";
5
5
  export declare function getNextStepToSpec(args: ArgPlaceholder): string;
6
+ export declare function getCustomContextStep(npxCommand: string, commandName: string): string;
6
7
  export type ArgPlaceholder = '$ARGUMENTS' | '{{args}}';
7
8
  export interface CommandDefinitionContext {
8
9
  args: ArgPlaceholder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dezkareid/osddt",
3
- "version": "1.9.0",
3
+ "version": "1.10.1",
4
4
  "description": "Package for Spec-Driven Development workflow",
5
5
  "keywords": [
6
6
  "spec-driven",
@@ -40,10 +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-config-ts-base": "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",
50
+ "husky": "9.1.7",
47
51
  "prettier": "3.8.1",
48
52
  "rollup": "4.56.0",
49
53
  "typescript": "5.9.3",
@@ -56,6 +60,7 @@
56
60
  "test:watch": "vitest",
57
61
  "lint": "eslint src",
58
62
  "format": "prettier --write src",
59
- "setup-local": "pnpm run build && npx osddt setup --agents claude --repo-type single"
63
+ "setup-local": "pnpm run build && npx osddt setup --agents claude --repo-type single",
64
+ "update-local": "pnpm run build && npx osddt update"
60
65
  }
61
66
  }