@applica-software-guru/sdd-core 1.8.1 → 1.8.2

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.
Files changed (57) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -3
  4. package/dist/index.js.map +1 -1
  5. package/dist/parser/bug-parser.d.ts.map +1 -1
  6. package/dist/parser/bug-parser.js +2 -1
  7. package/dist/parser/bug-parser.js.map +1 -1
  8. package/dist/parser/cr-parser.d.ts.map +1 -1
  9. package/dist/parser/cr-parser.js +2 -1
  10. package/dist/parser/cr-parser.js.map +1 -1
  11. package/dist/prompt/draft-prompt-generator.d.ts +1 -1
  12. package/dist/prompt/draft-prompt-generator.d.ts.map +1 -1
  13. package/dist/prompt/draft-prompt-generator.js +19 -35
  14. package/dist/prompt/draft-prompt-generator.js.map +1 -1
  15. package/dist/remote/api-client.d.ts +2 -2
  16. package/dist/remote/api-client.d.ts.map +1 -1
  17. package/dist/remote/api-client.js +4 -4
  18. package/dist/remote/api-client.js.map +1 -1
  19. package/dist/remote/sync-engine.js +12 -8
  20. package/dist/remote/sync-engine.js.map +1 -1
  21. package/dist/scaffold/skill-adapters.d.ts +0 -9
  22. package/dist/scaffold/skill-adapters.d.ts.map +1 -1
  23. package/dist/scaffold/skill-adapters.js +17 -0
  24. package/dist/scaffold/skill-adapters.js.map +1 -1
  25. package/dist/scaffold/templates.d.ts +1 -5
  26. package/dist/scaffold/templates.d.ts.map +1 -1
  27. package/dist/scaffold/templates.generated.d.ts +7 -0
  28. package/dist/scaffold/templates.generated.d.ts.map +1 -0
  29. package/dist/scaffold/templates.generated.js +464 -0
  30. package/dist/scaffold/templates.generated.js.map +1 -0
  31. package/dist/scaffold/templates.js +8 -338
  32. package/dist/scaffold/templates.js.map +1 -1
  33. package/dist/sdd.d.ts +0 -1
  34. package/dist/sdd.d.ts.map +1 -1
  35. package/dist/sdd.js +1 -18
  36. package/dist/sdd.js.map +1 -1
  37. package/package.json +2 -1
  38. package/scripts/generate-templates.mjs +38 -0
  39. package/src/index.ts +1 -1
  40. package/src/parser/bug-parser.ts +5 -3
  41. package/src/parser/cr-parser.ts +5 -3
  42. package/src/prompt/draft-prompt-generator.ts +18 -38
  43. package/src/remote/api-client.ts +2 -2
  44. package/src/remote/sync-engine.ts +15 -15
  45. package/src/scaffold/skill-adapters.ts +18 -11
  46. package/src/scaffold/templates.generated.ts +466 -0
  47. package/src/scaffold/templates.ts +9 -342
  48. package/src/sdd.ts +1 -19
  49. package/tests/agent-defaults.test.ts +24 -0
  50. package/tests/api-client.test.ts +6 -6
  51. package/tests/draft-prompt.test.ts +78 -0
  52. package/dist/prompt/apply-prompt-generator.d.ts +0 -4
  53. package/dist/prompt/apply-prompt-generator.d.ts.map +0 -1
  54. package/dist/prompt/apply-prompt-generator.js +0 -97
  55. package/dist/prompt/apply-prompt-generator.js.map +0 -1
  56. package/src/prompt/apply-prompt-generator.ts +0 -117
  57. package/tests/apply.test.ts +0 -117
@@ -8,67 +8,47 @@ export interface DraftElements {
8
8
 
9
9
  export function generateDraftEnrichmentPrompt(
10
10
  drafts: DraftElements,
11
- projectContext: StoryFile[],
12
- projectDescription: string,
13
11
  ): string | null {
14
12
  const totalDrafts = drafts.docs.length + drafts.crs.length + drafts.bugs.length;
15
13
  if (totalDrafts === 0) {
16
14
  return null;
17
15
  }
18
16
 
19
- const sections: string[] = [];
17
+ const sections: string[] = [
18
+ '# Draft Tasks',
19
+ '',
20
+ 'Enrich all draft elements listed below.',
21
+ 'Read each file directly from disk, complete missing details, and save it keeping `status: draft`.',
22
+ 'Do not mark statuses automatically in this step.',
23
+ '',
24
+ ];
20
25
 
21
- // Project context header
22
- sections.push(`# Draft Enrichment\n`);
23
- sections.push(`## Project\n\n${projectDescription}\n`);
24
-
25
- // Global context: all non-draft documents
26
- if (projectContext.length > 0) {
27
- const ctxLines = [`## Project context (${projectContext.length} documents)\n`];
28
- ctxLines.push('Use the following existing documents as context to produce complete, coherent documentation.\n');
29
- for (const f of projectContext) {
30
- ctxLines.push(`### \`${f.relativePath}\` — ${f.frontmatter.title}\n`);
31
- ctxLines.push(f.body.trim());
32
- ctxLines.push('');
33
- }
34
- sections.push(ctxLines.join('\n'));
35
- }
36
-
37
- // Draft documents
38
26
  if (drafts.docs.length > 0) {
39
- const lines = [`## Draft documents to enrich (${drafts.docs.length})\n`];
40
- lines.push('Each draft below contains incomplete human-written content. Produce a complete version for each document, preserving the original intent while adding missing details based on project context.\n');
27
+ const lines = [`## Draft documents (${drafts.docs.length})`];
41
28
  for (const f of drafts.docs) {
42
- lines.push(`### \`${f.relativePath}\` — ${f.frontmatter.title}\n`);
43
- lines.push(f.body.trim());
44
- lines.push('');
29
+ lines.push(`- \`${f.relativePath}\` — ${f.frontmatter.title}`);
45
30
  }
46
31
  sections.push(lines.join('\n'));
32
+ sections.push('');
47
33
  }
48
34
 
49
- // Draft change requests
50
35
  if (drafts.crs.length > 0) {
51
- const lines = [`## Draft change requests to enrich (${drafts.crs.length})\n`];
52
- lines.push('Each draft CR contains a rough description of requested changes. Produce a complete, actionable change request for each, specifying which documents are affected and what changes should be made.\n');
36
+ const lines = [`## Draft change requests (${drafts.crs.length})`];
53
37
  for (const cr of drafts.crs) {
54
- lines.push(`### \`${cr.relativePath}\` — ${cr.frontmatter.title}\n`);
55
- lines.push(cr.body.trim());
56
- lines.push('');
38
+ lines.push(`- \`${cr.relativePath}\` — ${cr.frontmatter.title}`);
57
39
  }
58
40
  sections.push(lines.join('\n'));
41
+ sections.push('');
59
42
  }
60
43
 
61
- // Draft bugs
62
44
  if (drafts.bugs.length > 0) {
63
- const lines = [`## Draft bugs to enrich (${drafts.bugs.length})\n`];
64
- lines.push('Each draft bug contains a rough description of an issue. Produce a complete bug report for each, including affected components, expected vs actual behavior, and steps to reproduce when possible.\n');
45
+ const lines = [`## Draft bugs (${drafts.bugs.length})`];
65
46
  for (const bug of drafts.bugs) {
66
- lines.push(`### \`${bug.relativePath}\` — ${bug.frontmatter.title}\n`);
67
- lines.push(bug.body.trim());
68
- lines.push('');
47
+ lines.push(`- \`${bug.relativePath}\` — ${bug.frontmatter.title}`);
69
48
  }
70
49
  sections.push(lines.join('\n'));
50
+ sections.push('');
71
51
  }
72
52
 
73
- return sections.join('\n\n');
53
+ return sections.join('\n');
74
54
  }
@@ -180,7 +180,7 @@ export async function pushDocs(
180
180
  }
181
181
 
182
182
  /** GET /cli/pending-crs */
183
- export async function fetchPendingCRs(config: ApiClientConfig): Promise<RemoteCRResponse[]> {
183
+ export async function pullPendingCRs(config: ApiClientConfig): Promise<RemoteCRResponse[]> {
184
184
  return request<RemoteCRResponse[]>(config, 'GET', '/cli/pending-crs');
185
185
  }
186
186
 
@@ -193,7 +193,7 @@ export async function pushCRs(
193
193
  }
194
194
 
195
195
  /** GET /cli/open-bugs */
196
- export async function fetchOpenBugs(config: ApiClientConfig): Promise<RemoteBugResponse[]> {
196
+ export async function pullOpenBugs(config: ApiClientConfig): Promise<RemoteBugResponse[]> {
197
197
  return request<RemoteBugResponse[]>(config, 'GET', '/cli/open-bugs');
198
198
  }
199
199
 
@@ -8,7 +8,7 @@ import { readConfig } from '../config/config-manager.js';
8
8
  import { parseAllStoryFiles } from '../parser/story-parser.js';
9
9
  import { parseAllCRFiles } from '../parser/cr-parser.js';
10
10
  import { parseAllBugFiles } from '../parser/bug-parser.js';
11
- import { buildApiConfig, pullDocs, pushDocs, pushCRs, pushBugs, deleteDocs, deleteCRs, deleteBugs, fetchPendingCRs, fetchOpenBugs, resetProject } from './api-client.js';
11
+ import { buildApiConfig, pullDocs, pushDocs, pushCRs, pushBugs, deleteDocs, deleteCRs, deleteBugs, pullPendingCRs, pullOpenBugs, resetProject } from './api-client.js';
12
12
  import { readRemoteState, writeRemoteState } from './state.js';
13
13
  import type {
14
14
  PushResult,
@@ -194,13 +194,13 @@ export async function pushToRemote(root: string, options?: PushOptions): Promise
194
194
  const crResult = await pushCRs(api, crPayload);
195
195
 
196
196
  for (const cr of crResult.change_requests) {
197
- const localPath = crsToPush.find(
198
- (f) => f.frontmatter.title === cr.title,
199
- )?.relativePath;
197
+ const localPath = cr.path
198
+ ?? crsToPush.find((f) => f.frontmatter.title === cr.title)?.relativePath;
200
199
  if (localPath) {
201
- const absPath = resolve(root, localPath);
200
+ const normalized = normalizePath(localPath);
201
+ const absPath = resolve(root, normalized);
202
202
  const rawContent = existsSync(absPath) ? await readFile(absPath, 'utf-8') : '';
203
- state.changeRequests![normalizePath(localPath)] = {
203
+ state.changeRequests![normalized] = {
204
204
  remoteId: cr.id,
205
205
  localHash: sha256(rawContent),
206
206
  lastSynced: new Date().toISOString(),
@@ -247,13 +247,13 @@ export async function pushToRemote(root: string, options?: PushOptions): Promise
247
247
  const bugResult = await pushBugs(api, bugPayload);
248
248
 
249
249
  for (const bug of bugResult.bugs) {
250
- const localPath = bugsToPush.find(
251
- (b) => b.frontmatter.title === bug.title,
252
- )?.relativePath;
250
+ const localPath = bug.path
251
+ ?? bugsToPush.find((b) => b.frontmatter.title === bug.title)?.relativePath;
253
252
  if (localPath) {
254
- const absPath = resolve(root, localPath);
253
+ const normalized = normalizePath(localPath);
254
+ const absPath = resolve(root, normalized);
255
255
  const rawContent = existsSync(absPath) ? await readFile(absPath, 'utf-8') : '';
256
- state.bugs![normalizePath(localPath)] = {
256
+ state.bugs![normalized] = {
257
257
  remoteId: bug.id,
258
258
  localHash: sha256(rawContent),
259
259
  lastSynced: new Date().toISOString(),
@@ -432,7 +432,7 @@ export async function pullCRsFromRemote(root: string, timeout?: number): Promise
432
432
  const config = await readConfig(root);
433
433
  const api = buildApiConfig(config, timeout);
434
434
 
435
- const remoteCRs = await fetchPendingCRs(api);
435
+ const remoteCRs = await pullPendingCRs(api);
436
436
  const state = await readRemoteState(root);
437
437
  if (!state.changeRequests) state.changeRequests = {};
438
438
 
@@ -448,7 +448,7 @@ export async function pullCRsFromRemote(root: string, timeout?: number): Promise
448
448
 
449
449
  // Detect remote deletions: tracked locally but not in remote response
450
450
  const remoteCRIdSet = new Set(remoteCRs.map((cr) => cr.id));
451
- for (const [localPath, tracked] of Object.entries(state.changeRequests!)) {
451
+ for (const [localPath, tracked] of Object.entries(state.changeRequests)) {
452
452
  if (!remoteCRIdSet.has(tracked.remoteId)) {
453
453
  const absPath = resolve(root, localPath);
454
454
  if (existsSync(absPath)) {
@@ -522,7 +522,7 @@ export async function pullBugsFromRemote(root: string, timeout?: number): Promis
522
522
  const config = await readConfig(root);
523
523
  const api = buildApiConfig(config, timeout);
524
524
 
525
- const remoteBugs = await fetchOpenBugs(api);
525
+ const remoteBugs = await pullOpenBugs(api);
526
526
  const state = await readRemoteState(root);
527
527
  if (!state.bugs) state.bugs = {};
528
528
 
@@ -538,7 +538,7 @@ export async function pullBugsFromRemote(root: string, timeout?: number): Promis
538
538
 
539
539
  // Detect remote deletions: tracked locally but not in remote response
540
540
  const remoteBugIdSet = new Set(remoteBugs.map((b) => b.id));
541
- for (const [localPath, tracked] of Object.entries(state.bugs!)) {
541
+ for (const [localPath, tracked] of Object.entries(state.bugs)) {
542
542
  if (!remoteBugIdSet.has(tracked.remoteId)) {
543
543
  const absPath = resolve(root, localPath);
544
544
  if (existsSync(absPath)) {
@@ -6,6 +6,7 @@ import {
6
6
  CHANGE_REQUESTS_REFERENCE,
7
7
  FILE_FORMAT_REFERENCE,
8
8
  SKILL_MD_TEMPLATE,
9
+ SKILL_REMOTE_MD_TEMPLATE,
9
10
  SKILL_UI_MD_TEMPLATE,
10
11
  } from "./templates.js";
11
12
 
@@ -81,22 +82,20 @@ const CANONICAL_SKILLS: Array<{
81
82
  },
82
83
  ],
83
84
  },
85
+ {
86
+ id: "sdd-remote",
87
+ files: [
88
+ {
89
+ path: `${CANONICAL_BASE_DIR}/sdd-remote/SKILL.md`,
90
+ content: SKILL_REMOTE_MD_TEMPLATE,
91
+ },
92
+ ],
93
+ },
84
94
  ];
85
95
 
86
96
  const CANONICAL_FILES: Array<{ path: string; content: string }> =
87
97
  CANONICAL_SKILLS.flatMap((skill) => skill.files);
88
98
 
89
- export interface SkillPathsDefinition {
90
- skillId: string;
91
- paths: string[];
92
- }
93
-
94
- export interface SkillAdapterDefinition {
95
- id: string;
96
- mode: AdapterMode;
97
- skills: SkillPathsDefinition[];
98
- }
99
-
100
99
  export const SKILL_ADAPTERS: SkillAdapterDefinition[] = [
101
100
  {
102
101
  id: "claude",
@@ -115,6 +114,10 @@ export const SKILL_ADAPTERS: SkillAdapterDefinition[] = [
115
114
  skillId: "sdd-ui",
116
115
  paths: [".claude/skills/sdd-ui/SKILL.md"],
117
116
  },
117
+ {
118
+ skillId: "sdd-remote",
119
+ paths: [".claude/skills/sdd-remote/SKILL.md"],
120
+ },
118
121
  ],
119
122
  },
120
123
  {
@@ -134,6 +137,10 @@ export const SKILL_ADAPTERS: SkillAdapterDefinition[] = [
134
137
  skillId: "sdd-ui",
135
138
  paths: [".agents/skills/sdd-ui/SKILL.md"],
136
139
  },
140
+ {
141
+ skillId: "sdd-remote",
142
+ paths: [".agents/skills/sdd-remote/SKILL.md"],
143
+ },
137
144
  ],
138
145
  },
139
146
  ];