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

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 (90) hide show
  1. package/dist/agent/agent-defaults.js +2 -2
  2. package/dist/agent/agent-defaults.js.map +1 -1
  3. package/dist/agent/agent-runner.d.ts +17 -0
  4. package/dist/agent/agent-runner.d.ts.map +1 -1
  5. package/dist/agent/agent-runner.js +163 -11
  6. package/dist/agent/agent-runner.js.map +1 -1
  7. package/dist/agent/worker-daemon.d.ts +12 -0
  8. package/dist/agent/worker-daemon.d.ts.map +1 -0
  9. package/dist/agent/worker-daemon.js +220 -0
  10. package/dist/agent/worker-daemon.js.map +1 -0
  11. package/dist/git/git.d.ts +6 -0
  12. package/dist/git/git.d.ts.map +1 -1
  13. package/dist/git/git.js +60 -0
  14. package/dist/git/git.js.map +1 -1
  15. package/dist/index.d.ts +8 -3
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +19 -3
  18. package/dist/index.js.map +1 -1
  19. package/dist/parser/bug-parser.d.ts.map +1 -1
  20. package/dist/parser/bug-parser.js +2 -1
  21. package/dist/parser/bug-parser.js.map +1 -1
  22. package/dist/parser/cr-parser.d.ts.map +1 -1
  23. package/dist/parser/cr-parser.js +2 -1
  24. package/dist/parser/cr-parser.js.map +1 -1
  25. package/dist/prompt/draft-prompt-generator.d.ts +1 -1
  26. package/dist/prompt/draft-prompt-generator.d.ts.map +1 -1
  27. package/dist/prompt/draft-prompt-generator.js +23 -35
  28. package/dist/prompt/draft-prompt-generator.js.map +1 -1
  29. package/dist/prompt/prompt-generator.d.ts.map +1 -1
  30. package/dist/prompt/prompt-generator.js +5 -0
  31. package/dist/prompt/prompt-generator.js.map +1 -1
  32. package/dist/remote/api-client.d.ts +2 -2
  33. package/dist/remote/api-client.d.ts.map +1 -1
  34. package/dist/remote/api-client.js +4 -4
  35. package/dist/remote/api-client.js.map +1 -1
  36. package/dist/remote/sync-engine.d.ts.map +1 -1
  37. package/dist/remote/sync-engine.js +13 -8
  38. package/dist/remote/sync-engine.js.map +1 -1
  39. package/dist/remote/worker-client.d.ts +22 -0
  40. package/dist/remote/worker-client.d.ts.map +1 -0
  41. package/dist/remote/worker-client.js +88 -0
  42. package/dist/remote/worker-client.js.map +1 -0
  43. package/dist/remote/worker-types.d.ts +38 -0
  44. package/dist/remote/worker-types.d.ts.map +1 -0
  45. package/dist/remote/worker-types.js +3 -0
  46. package/dist/remote/worker-types.js.map +1 -0
  47. package/dist/scaffold/skill-adapters.d.ts +0 -9
  48. package/dist/scaffold/skill-adapters.d.ts.map +1 -1
  49. package/dist/scaffold/skill-adapters.js +17 -0
  50. package/dist/scaffold/skill-adapters.js.map +1 -1
  51. package/dist/scaffold/templates.d.ts +1 -5
  52. package/dist/scaffold/templates.d.ts.map +1 -1
  53. package/dist/scaffold/templates.generated.d.ts +7 -0
  54. package/dist/scaffold/templates.generated.d.ts.map +1 -0
  55. package/dist/scaffold/templates.generated.js +482 -0
  56. package/dist/scaffold/templates.generated.js.map +1 -0
  57. package/dist/scaffold/templates.js +8 -338
  58. package/dist/scaffold/templates.js.map +1 -1
  59. package/dist/sdd.d.ts +0 -1
  60. package/dist/sdd.d.ts.map +1 -1
  61. package/dist/sdd.js +1 -18
  62. package/dist/sdd.js.map +1 -1
  63. package/package.json +2 -1
  64. package/scripts/generate-templates.mjs +38 -0
  65. package/src/agent/agent-defaults.ts +2 -2
  66. package/src/agent/agent-runner.ts +184 -12
  67. package/src/agent/worker-daemon.ts +254 -0
  68. package/src/git/git.ts +61 -0
  69. package/src/index.ts +8 -3
  70. package/src/parser/bug-parser.ts +5 -3
  71. package/src/parser/cr-parser.ts +5 -3
  72. package/src/prompt/draft-prompt-generator.ts +26 -38
  73. package/src/prompt/prompt-generator.ts +8 -0
  74. package/src/remote/api-client.ts +2 -2
  75. package/src/remote/sync-engine.ts +16 -15
  76. package/src/remote/worker-client.ts +141 -0
  77. package/src/remote/worker-types.ts +40 -0
  78. package/src/scaffold/skill-adapters.ts +18 -11
  79. package/src/scaffold/templates.generated.ts +484 -0
  80. package/src/scaffold/templates.ts +9 -342
  81. package/src/sdd.ts +1 -19
  82. package/tests/agent-defaults.test.ts +24 -0
  83. package/tests/api-client.test.ts +6 -6
  84. package/tests/draft-prompt.test.ts +78 -0
  85. package/dist/prompt/apply-prompt-generator.d.ts +0 -4
  86. package/dist/prompt/apply-prompt-generator.d.ts.map +0 -1
  87. package/dist/prompt/apply-prompt-generator.js +0 -97
  88. package/dist/prompt/apply-prompt-generator.js.map +0 -1
  89. package/src/prompt/apply-prompt-generator.ts +0 -117
  90. package/tests/apply.test.ts +0 -117
@@ -8,67 +8,55 @@ 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
+ sections.push(
54
+ '## Report\n',
55
+ 'At the end of the enrichment, provide a detailed report including:\n' +
56
+ '- List of enriched files (documents, CRs, bugs)\n' +
57
+ '- For each file: what was added or completed\n' +
58
+ '- Any ambiguities resolved or assumptions made'
59
+ );
60
+
61
+ return sections.join('\n');
74
62
  }
@@ -35,5 +35,13 @@ export function generatePrompt(files: StoryFile[], root?: string): string {
35
35
  sections.push(delLines.join('\n'));
36
36
  }
37
37
 
38
+ sections.push(
39
+ '## Report\n\n' +
40
+ 'At the end of your work, provide a detailed report including:\n' +
41
+ '- List of files created, modified, or deleted\n' +
42
+ '- Description of actions taken for each file\n' +
43
+ '- Any issues encountered or decisions made'
44
+ );
45
+
38
46
  return sections.join('\n\n');
39
47
  }
@@ -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,
@@ -139,6 +139,7 @@ export async function pushToRemote(root: string, options?: PushOptions): Promise
139
139
  path: normalizePath(f.relativePath),
140
140
  title: f.frontmatter.title,
141
141
  content: f.body,
142
+ status: f.frontmatter.status,
142
143
  }));
143
144
 
144
145
  const result = await pushDocs(api, documents);
@@ -194,13 +195,13 @@ export async function pushToRemote(root: string, options?: PushOptions): Promise
194
195
  const crResult = await pushCRs(api, crPayload);
195
196
 
196
197
  for (const cr of crResult.change_requests) {
197
- const localPath = crsToPush.find(
198
- (f) => f.frontmatter.title === cr.title,
199
- )?.relativePath;
198
+ const localPath = cr.path
199
+ ?? crsToPush.find((f) => f.frontmatter.title === cr.title)?.relativePath;
200
200
  if (localPath) {
201
- const absPath = resolve(root, localPath);
201
+ const normalized = normalizePath(localPath);
202
+ const absPath = resolve(root, normalized);
202
203
  const rawContent = existsSync(absPath) ? await readFile(absPath, 'utf-8') : '';
203
- state.changeRequests![normalizePath(localPath)] = {
204
+ state.changeRequests![normalized] = {
204
205
  remoteId: cr.id,
205
206
  localHash: sha256(rawContent),
206
207
  lastSynced: new Date().toISOString(),
@@ -247,13 +248,13 @@ export async function pushToRemote(root: string, options?: PushOptions): Promise
247
248
  const bugResult = await pushBugs(api, bugPayload);
248
249
 
249
250
  for (const bug of bugResult.bugs) {
250
- const localPath = bugsToPush.find(
251
- (b) => b.frontmatter.title === bug.title,
252
- )?.relativePath;
251
+ const localPath = bug.path
252
+ ?? bugsToPush.find((b) => b.frontmatter.title === bug.title)?.relativePath;
253
253
  if (localPath) {
254
- const absPath = resolve(root, localPath);
254
+ const normalized = normalizePath(localPath);
255
+ const absPath = resolve(root, normalized);
255
256
  const rawContent = existsSync(absPath) ? await readFile(absPath, 'utf-8') : '';
256
- state.bugs![normalizePath(localPath)] = {
257
+ state.bugs![normalized] = {
257
258
  remoteId: bug.id,
258
259
  localHash: sha256(rawContent),
259
260
  lastSynced: new Date().toISOString(),
@@ -432,7 +433,7 @@ export async function pullCRsFromRemote(root: string, timeout?: number): Promise
432
433
  const config = await readConfig(root);
433
434
  const api = buildApiConfig(config, timeout);
434
435
 
435
- const remoteCRs = await fetchPendingCRs(api);
436
+ const remoteCRs = await pullPendingCRs(api);
436
437
  const state = await readRemoteState(root);
437
438
  if (!state.changeRequests) state.changeRequests = {};
438
439
 
@@ -448,7 +449,7 @@ export async function pullCRsFromRemote(root: string, timeout?: number): Promise
448
449
 
449
450
  // Detect remote deletions: tracked locally but not in remote response
450
451
  const remoteCRIdSet = new Set(remoteCRs.map((cr) => cr.id));
451
- for (const [localPath, tracked] of Object.entries(state.changeRequests!)) {
452
+ for (const [localPath, tracked] of Object.entries(state.changeRequests)) {
452
453
  if (!remoteCRIdSet.has(tracked.remoteId)) {
453
454
  const absPath = resolve(root, localPath);
454
455
  if (existsSync(absPath)) {
@@ -522,7 +523,7 @@ export async function pullBugsFromRemote(root: string, timeout?: number): Promis
522
523
  const config = await readConfig(root);
523
524
  const api = buildApiConfig(config, timeout);
524
525
 
525
- const remoteBugs = await fetchOpenBugs(api);
526
+ const remoteBugs = await pullOpenBugs(api);
526
527
  const state = await readRemoteState(root);
527
528
  if (!state.bugs) state.bugs = {};
528
529
 
@@ -538,7 +539,7 @@ export async function pullBugsFromRemote(root: string, timeout?: number): Promis
538
539
 
539
540
  // Detect remote deletions: tracked locally but not in remote response
540
541
  const remoteBugIdSet = new Set(remoteBugs.map((b) => b.id));
541
- for (const [localPath, tracked] of Object.entries(state.bugs!)) {
542
+ for (const [localPath, tracked] of Object.entries(state.bugs)) {
542
543
  if (!remoteBugIdSet.has(tracked.remoteId)) {
543
544
  const absPath = resolve(root, localPath);
544
545
  if (existsSync(absPath)) {
@@ -0,0 +1,141 @@
1
+ import type { ApiClientConfig } from './api-client.js';
2
+ import type { WorkerRegistration, WorkerJobAssignment, WorkerJobAnswer } from './worker-types.js';
3
+
4
+ /**
5
+ * Low-level fetch wrapper for worker endpoints.
6
+ * Unlike the main api-client, worker requests have different timeout needs
7
+ * (e.g. long-poll uses 35s timeout) and don't need full retry logic.
8
+ */
9
+ async function workerRequest<T>(
10
+ config: ApiClientConfig,
11
+ method: string,
12
+ path: string,
13
+ body?: unknown,
14
+ timeoutMs?: number,
15
+ ): Promise<T> {
16
+ const url = `${config.baseUrl}${path}`;
17
+ const controller = new AbortController();
18
+ const timer = setTimeout(() => controller.abort(), timeoutMs ?? 10_000);
19
+
20
+ try {
21
+ const res = await fetch(url, {
22
+ method,
23
+ headers: {
24
+ Authorization: `Bearer ${config.apiKey}`,
25
+ 'Content-Type': 'application/json',
26
+ },
27
+ body: body != null ? JSON.stringify(body) : undefined,
28
+ signal: controller.signal,
29
+ });
30
+ clearTimeout(timer);
31
+
32
+ if (!res.ok) {
33
+ let detail: string;
34
+ try {
35
+ const err = (await res.json()) as { detail?: string };
36
+ detail = err.detail ?? res.statusText;
37
+ } catch {
38
+ detail = res.statusText;
39
+ }
40
+ throw new Error(`Worker API error ${res.status}: ${detail}`);
41
+ }
42
+
43
+ // 204 No Content (poll with no job)
44
+ if (res.status === 204) {
45
+ return null as T;
46
+ }
47
+
48
+ return (await res.json()) as T;
49
+ } catch (error) {
50
+ clearTimeout(timer);
51
+ throw error;
52
+ }
53
+ }
54
+
55
+ /** POST /cli/workers/register */
56
+ export async function registerWorker(
57
+ config: ApiClientConfig,
58
+ name: string,
59
+ agent: string,
60
+ branch?: string,
61
+ metadata?: Record<string, unknown>,
62
+ ): Promise<WorkerRegistration> {
63
+ return workerRequest<WorkerRegistration>(
64
+ config, 'POST', '/cli/workers/register',
65
+ { name, agent, branch, metadata },
66
+ );
67
+ }
68
+
69
+ /** POST /cli/workers/:workerId/heartbeat */
70
+ export async function workerHeartbeat(
71
+ config: ApiClientConfig,
72
+ workerId: string,
73
+ workerStatus: 'online' | 'busy' = 'online',
74
+ ): Promise<void> {
75
+ await workerRequest(
76
+ config, 'POST', `/cli/workers/${workerId}/heartbeat`,
77
+ { status: workerStatus },
78
+ );
79
+ }
80
+
81
+ /** GET /cli/workers/:workerId/poll — long-poll (up to 35s timeout) */
82
+ export async function workerPoll(
83
+ config: ApiClientConfig,
84
+ workerId: string,
85
+ ): Promise<WorkerJobAssignment | null> {
86
+ return workerRequest<WorkerJobAssignment | null>(
87
+ config, 'GET', `/cli/workers/${workerId}/poll`,
88
+ undefined,
89
+ 35_000, // 35s to exceed server's 30s hold time
90
+ );
91
+ }
92
+
93
+ /** POST /cli/workers/jobs/:jobId/started */
94
+ export async function workerJobStarted(
95
+ config: ApiClientConfig,
96
+ jobId: string,
97
+ ): Promise<void> {
98
+ await workerRequest(config, 'POST', `/cli/workers/jobs/${jobId}/started`);
99
+ }
100
+
101
+ /** POST /cli/workers/jobs/:jobId/output */
102
+ export async function workerJobOutput(
103
+ config: ApiClientConfig,
104
+ jobId: string,
105
+ lines: string[],
106
+ ): Promise<void> {
107
+ await workerRequest(config, 'POST', `/cli/workers/jobs/${jobId}/output`, { lines });
108
+ }
109
+
110
+ /** POST /cli/workers/jobs/:jobId/question */
111
+ export async function workerJobQuestion(
112
+ config: ApiClientConfig,
113
+ jobId: string,
114
+ content: string,
115
+ ): Promise<void> {
116
+ await workerRequest(config, 'POST', `/cli/workers/jobs/${jobId}/question`, { content });
117
+ }
118
+
119
+ /** GET /cli/workers/jobs/:jobId/answers?after_sequence=N */
120
+ export async function workerJobAnswers(
121
+ config: ApiClientConfig,
122
+ jobId: string,
123
+ afterSequence: number = 0,
124
+ ): Promise<WorkerJobAnswer[]> {
125
+ return workerRequest<WorkerJobAnswer[]>(
126
+ config, 'GET', `/cli/workers/jobs/${jobId}/answers?after_sequence=${afterSequence}`,
127
+ );
128
+ }
129
+
130
+ /** POST /cli/workers/jobs/:jobId/completed */
131
+ export async function workerJobCompleted(
132
+ config: ApiClientConfig,
133
+ jobId: string,
134
+ exitCode: number,
135
+ changedFiles?: Array<{ path: string; status: 'new' | 'modified' | 'deleted' }>,
136
+ ): Promise<void> {
137
+ await workerRequest(config, 'POST', `/cli/workers/jobs/${jobId}/completed`, {
138
+ exit_code: exitCode,
139
+ changed_files: changedFiles ?? [],
140
+ });
141
+ }
@@ -0,0 +1,40 @@
1
+ /** Worker registration response from the server */
2
+ export interface WorkerRegistration {
3
+ id: string;
4
+ project_id: string;
5
+ name: string;
6
+ status: string;
7
+ agent: string;
8
+ branch?: string;
9
+ last_heartbeat_at: string | null;
10
+ registered_at: string;
11
+ is_online: boolean;
12
+ }
13
+
14
+ /** Job assignment returned by the poll endpoint */
15
+ export interface WorkerJobAssignment {
16
+ job_id: string;
17
+ entity_type: string | null;
18
+ entity_id: string | null;
19
+ prompt: string;
20
+ agent: string;
21
+ model?: string;
22
+ branch?: string;
23
+ }
24
+
25
+ /** Answer message from the user */
26
+ export interface WorkerJobAnswer {
27
+ id: string;
28
+ job_id: string;
29
+ kind: 'answer';
30
+ content: string;
31
+ sequence: number;
32
+ created_at: string;
33
+ }
34
+
35
+ /** Local worker state persisted in .sdd/worker.json */
36
+ export interface WorkerState {
37
+ workerId: string;
38
+ name: string;
39
+ registeredAt: string;
40
+ }
@@ -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
  ];