@applica-software-guru/sdd-core 1.8.2 → 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.
- package/dist/agent/agent-defaults.js +2 -2
- package/dist/agent/agent-defaults.js.map +1 -1
- package/dist/agent/agent-runner.d.ts +17 -0
- package/dist/agent/agent-runner.d.ts.map +1 -1
- package/dist/agent/agent-runner.js +163 -11
- package/dist/agent/agent-runner.js.map +1 -1
- package/dist/agent/worker-daemon.d.ts +12 -0
- package/dist/agent/worker-daemon.d.ts.map +1 -0
- package/dist/agent/worker-daemon.js +220 -0
- package/dist/agent/worker-daemon.js.map +1 -0
- package/dist/git/git.d.ts +6 -0
- package/dist/git/git.d.ts.map +1 -1
- package/dist/git/git.js +60 -0
- package/dist/git/git.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/prompt/draft-prompt-generator.d.ts.map +1 -1
- package/dist/prompt/draft-prompt-generator.js +4 -0
- package/dist/prompt/draft-prompt-generator.js.map +1 -1
- package/dist/prompt/prompt-generator.d.ts.map +1 -1
- package/dist/prompt/prompt-generator.js +5 -0
- package/dist/prompt/prompt-generator.js.map +1 -1
- package/dist/remote/sync-engine.d.ts.map +1 -1
- package/dist/remote/sync-engine.js +1 -0
- package/dist/remote/sync-engine.js.map +1 -1
- package/dist/remote/worker-client.d.ts +22 -0
- package/dist/remote/worker-client.d.ts.map +1 -0
- package/dist/remote/worker-client.js +88 -0
- package/dist/remote/worker-client.js.map +1 -0
- package/dist/remote/worker-types.d.ts +38 -0
- package/dist/remote/worker-types.d.ts.map +1 -0
- package/dist/remote/worker-types.js +3 -0
- package/dist/remote/worker-types.js.map +1 -0
- package/dist/scaffold/templates.generated.d.ts +1 -1
- package/dist/scaffold/templates.generated.d.ts.map +1 -1
- package/dist/scaffold/templates.generated.js +51 -33
- package/dist/scaffold/templates.generated.js.map +1 -1
- package/package.json +1 -1
- package/src/agent/agent-defaults.ts +2 -2
- package/src/agent/agent-runner.ts +184 -12
- package/src/agent/worker-daemon.ts +254 -0
- package/src/git/git.ts +61 -0
- package/src/index.ts +7 -2
- package/src/prompt/draft-prompt-generator.ts +8 -0
- package/src/prompt/prompt-generator.ts +8 -0
- package/src/remote/sync-engine.ts +1 -0
- package/src/remote/worker-client.ts +141 -0
- package/src/remote/worker-types.ts +40 -0
- package/src/scaffold/templates.generated.ts +51 -33
package/src/git/git.ts
CHANGED
|
@@ -85,6 +85,67 @@ export function getGitModifiedFiles(root: string): Set<string> {
|
|
|
85
85
|
return modified;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
export function getCurrentBranch(root: string): string | null {
|
|
89
|
+
try {
|
|
90
|
+
return run('git rev-parse --abbrev-ref HEAD', root) || null;
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function checkoutBranch(root: string, branch: string): void {
|
|
97
|
+
try {
|
|
98
|
+
run(`git checkout ${branch}`, root);
|
|
99
|
+
} catch {
|
|
100
|
+
// branch doesn't exist — create it
|
|
101
|
+
run(`git checkout -b ${branch}`, root);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getJobChangedFiles(
|
|
106
|
+
root: string,
|
|
107
|
+
fromCommit: string | null,
|
|
108
|
+
): Array<{ path: string; status: 'new' | 'modified' | 'deleted' }> {
|
|
109
|
+
const result = new Map<string, 'new' | 'modified' | 'deleted'>();
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
// Committed changes since the base commit
|
|
113
|
+
if (fromCommit) {
|
|
114
|
+
const committed = run(`git diff --name-status ${fromCommit} HEAD`, root);
|
|
115
|
+
for (const line of committed.split('\n').filter(Boolean)) {
|
|
116
|
+
const parts = line.split('\t');
|
|
117
|
+
const flag = parts[0][0]; // first char (A/M/D/R/C)
|
|
118
|
+
// Renames have two paths: old\tnew — use the new path
|
|
119
|
+
const path = parts.length >= 3 ? parts[2] : parts[1];
|
|
120
|
+
const fileStatus: 'new' | 'modified' | 'deleted' =
|
|
121
|
+
flag === 'A' ? 'new' : flag === 'D' ? 'deleted' : 'modified';
|
|
122
|
+
result.set(path, fileStatus);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Uncommitted working tree changes (staged + unstaged + untracked)
|
|
127
|
+
const porcelain = run('git status --porcelain', root);
|
|
128
|
+
for (const line of porcelain.split('\n').filter(Boolean)) {
|
|
129
|
+
const xy = line.substring(0, 2);
|
|
130
|
+
const rawPath = line.substring(3).trim();
|
|
131
|
+
// Renamed files show as "old -> new" in porcelain v1
|
|
132
|
+
const path = rawPath.includes(' -> ') ? rawPath.split(' -> ')[1] : rawPath;
|
|
133
|
+
|
|
134
|
+
if (xy[0] === 'D' || xy[1] === 'D') {
|
|
135
|
+
result.set(path, 'deleted');
|
|
136
|
+
} else if (xy === '??' || xy[0] === 'A' || xy[1] === 'A') {
|
|
137
|
+
if (!result.has(path)) result.set(path, 'new');
|
|
138
|
+
} else {
|
|
139
|
+
if (!result.has(path)) result.set(path, 'modified');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
// not a git repo or no commits — return empty
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return Array.from(result.entries()).map(([path, status]) => ({ path, status }));
|
|
147
|
+
}
|
|
148
|
+
|
|
88
149
|
export function getChangedFiles(root: string, fromCommit: string | null): Array<{ path: string; status: 'new' | 'modified' | 'deleted' }> {
|
|
89
150
|
try {
|
|
90
151
|
if (!fromCommit) {
|
package/src/index.ts
CHANGED
|
@@ -21,8 +21,11 @@ export type {
|
|
|
21
21
|
export { SDDError, LockFileNotFoundError, ParseError, ProjectNotInitializedError, RemoteError, RemoteNotConfiguredError, RemoteTimeoutError } from "./errors.js";
|
|
22
22
|
export type { ProjectInfo } from "./scaffold/templates.js";
|
|
23
23
|
export { isSDDProject, readConfig, writeConfig } from "./config/config-manager.js";
|
|
24
|
-
export {
|
|
25
|
-
export
|
|
24
|
+
export { getCurrentBranch, checkoutBranch } from "./git/git.js";
|
|
25
|
+
export { runAgent, startAgent } from "./agent/agent-runner.js";
|
|
26
|
+
export type { AgentRunnerOptions, AgentRunnerHandle } from "./agent/agent-runner.js";
|
|
27
|
+
export { startWorkerDaemon } from "./agent/worker-daemon.js";
|
|
28
|
+
export type { WorkerDaemonOptions } from "./agent/worker-daemon.js";
|
|
26
29
|
export { DEFAULT_AGENTS, resolveAgentCommand } from "./agent/agent-defaults.js";
|
|
27
30
|
export { listSupportedAdapters, SKILL_ADAPTERS, syncSkillAdapters } from "./scaffold/skill-adapters.js";
|
|
28
31
|
export type {
|
|
@@ -39,6 +42,8 @@ export { generateDraftEnrichmentPrompt } from "./prompt/draft-prompt-generator.j
|
|
|
39
42
|
export type { DraftElements } from "./prompt/draft-prompt-generator.js";
|
|
40
43
|
export { resolveApiKey, buildApiConfig, pullDocs, pushDocs, pullPendingCRs, pullOpenBugs, markCRAppliedRemote, markBugResolvedRemote, markDocEnriched, markCREnriched, markBugEnriched, resetProject, DEFAULT_REMOTE_TIMEOUT } from "./remote/api-client.js";
|
|
41
44
|
export type { ApiClientConfig } from "./remote/api-client.js";
|
|
45
|
+
export { registerWorker, workerHeartbeat, workerPoll, workerJobStarted, workerJobOutput, workerJobQuestion, workerJobAnswers, workerJobCompleted } from "./remote/worker-client.js";
|
|
46
|
+
export type { WorkerRegistration, WorkerJobAssignment, WorkerJobAnswer, WorkerState } from "./remote/worker-types.js";
|
|
42
47
|
export { readRemoteState, writeRemoteState } from "./remote/state.js";
|
|
43
48
|
export { pushToRemote, pullFromRemote, pullCRsFromRemote, pullBugsFromRemote, getRemoteStatus, resetRemoteProject } from "./remote/sync-engine.js";
|
|
44
49
|
export type {
|
|
@@ -50,5 +50,13 @@ export function generateDraftEnrichmentPrompt(
|
|
|
50
50
|
sections.push('');
|
|
51
51
|
}
|
|
52
52
|
|
|
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
|
+
|
|
53
61
|
return sections.join('\n');
|
|
54
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
|
}
|
|
@@ -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);
|
|
@@ -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
|
+
}
|
|
@@ -228,13 +228,14 @@ name: sdd-remote
|
|
|
228
228
|
description: >
|
|
229
229
|
Remote sync workflow for Story Driven Development. Use when the user asks
|
|
230
230
|
to update local state from remote changes, process remote drafts, and push
|
|
231
|
-
enriched items back.
|
|
231
|
+
enriched items back. Also applies when running a remote worker job (enrich
|
|
232
|
+
or sync).
|
|
232
233
|
license: MIT
|
|
233
234
|
compatibility: Requires sdd CLI (npm i -g @applica-software-guru/sdd)
|
|
234
235
|
allowed-tools: Bash(sdd:*) Read Glob Grep
|
|
235
236
|
metadata:
|
|
236
237
|
author: applica-software-guru
|
|
237
|
-
version: "1.
|
|
238
|
+
version: "1.1"
|
|
238
239
|
---
|
|
239
240
|
|
|
240
241
|
# SDD Remote - Pull, Enrich, Push
|
|
@@ -244,6 +245,9 @@ metadata:
|
|
|
244
245
|
Use this skill to synchronize local SDD docs with remote updates, enrich draft content,
|
|
245
246
|
and publish the enriched result to remote in active states.
|
|
246
247
|
|
|
248
|
+
This skill also applies when a **remote worker job** is dispatched from SDD Flow, as the
|
|
249
|
+
worker runs these same workflows on behalf of the user.
|
|
250
|
+
|
|
247
251
|
## Detection
|
|
248
252
|
|
|
249
253
|
This workflow applies when:
|
|
@@ -251,79 +255,91 @@ This workflow applies when:
|
|
|
251
255
|
- \`.sdd/config.yaml\` exists in the project root
|
|
252
256
|
- The user asks to update local state from remote, pull pending CRs/bugs/docs,
|
|
253
257
|
enrich drafts, or push pending remote updates
|
|
258
|
+
- A remote worker job prompt instructs you to follow this workflow
|
|
254
259
|
|
|
255
|
-
##
|
|
260
|
+
## Workflows
|
|
261
|
+
|
|
262
|
+
### Enrich Workflow (CR)
|
|
256
263
|
|
|
257
|
-
Follow this sequence
|
|
264
|
+
Follow this sequence to enrich a draft Change Request:
|
|
258
265
|
|
|
259
|
-
1.
|
|
266
|
+
1. Pull remote updates:
|
|
260
267
|
|
|
261
268
|
\`\`\`bash
|
|
262
|
-
|
|
269
|
+
sdd pull --crs-only
|
|
263
270
|
\`\`\`
|
|
264
271
|
|
|
265
|
-
|
|
272
|
+
3. Generate draft TODO list:
|
|
266
273
|
|
|
267
274
|
\`\`\`bash
|
|
268
|
-
sdd
|
|
275
|
+
sdd drafts
|
|
269
276
|
\`\`\`
|
|
270
277
|
|
|
271
|
-
|
|
278
|
+
4. Enrich the draft with technical details, acceptance criteria, edge cases, and
|
|
279
|
+
any relevant information from the project documentation and comments.
|
|
272
280
|
|
|
273
|
-
|
|
281
|
+
5. Transition the enriched CR to pending:
|
|
274
282
|
|
|
275
283
|
\`\`\`bash
|
|
276
|
-
sdd
|
|
284
|
+
sdd mark-drafts-enriched
|
|
277
285
|
\`\`\`
|
|
278
286
|
|
|
279
|
-
|
|
287
|
+
This performs: \`draft → pending\`
|
|
288
|
+
|
|
289
|
+
6. Push the enriched content:
|
|
280
290
|
|
|
281
291
|
\`\`\`bash
|
|
282
|
-
sdd
|
|
283
|
-
sdd pull --crs-only
|
|
284
|
-
sdd pull --bugs-only
|
|
292
|
+
sdd push
|
|
285
293
|
\`\`\`
|
|
286
294
|
|
|
287
|
-
|
|
295
|
+
### Enrich Workflow (Document)
|
|
296
|
+
|
|
297
|
+
Follow this sequence to enrich a document:
|
|
298
|
+
|
|
299
|
+
1. Pull remote updates:
|
|
288
300
|
|
|
289
301
|
\`\`\`bash
|
|
290
|
-
sdd
|
|
302
|
+
sdd pull --docs-only
|
|
291
303
|
\`\`\`
|
|
292
304
|
|
|
293
|
-
|
|
294
|
-
|
|
305
|
+
3. Locate the document file in \`product/\` or \`system/\` and update its content
|
|
306
|
+
with the enriched version.
|
|
295
307
|
|
|
296
|
-
4.
|
|
308
|
+
4. Push the enriched content:
|
|
297
309
|
|
|
298
310
|
\`\`\`bash
|
|
299
|
-
sdd
|
|
311
|
+
sdd push
|
|
300
312
|
\`\`\`
|
|
301
313
|
|
|
302
|
-
|
|
314
|
+
If the document was in \`draft\` status, it will transition to \`new\` on the server.
|
|
315
|
+
|
|
316
|
+
### Sync Workflow (Project-level)
|
|
303
317
|
|
|
304
|
-
|
|
305
|
-
- Change Request: \`draft -> pending\`
|
|
306
|
-
- Bug: \`draft -> open\`
|
|
318
|
+
Follow this sequence for a full project sync (all pending items):
|
|
307
319
|
|
|
308
|
-
|
|
320
|
+
1. Pull the latest specs:
|
|
309
321
|
|
|
310
322
|
\`\`\`bash
|
|
311
|
-
sdd
|
|
323
|
+
sdd pull
|
|
312
324
|
\`\`\`
|
|
313
325
|
|
|
314
|
-
|
|
326
|
+
3. Run the \`sdd\` skill — it handles the full loop: open bugs, pending CRs,
|
|
327
|
+
documentation sync, code implementation, mark-synced, and commit.
|
|
328
|
+
|
|
329
|
+
4. Push:
|
|
315
330
|
|
|
316
331
|
\`\`\`bash
|
|
317
|
-
sdd
|
|
332
|
+
sdd push
|
|
318
333
|
\`\`\`
|
|
319
334
|
|
|
320
335
|
## Rules
|
|
321
336
|
|
|
322
337
|
1. Always check remote configuration before pull/push (\`sdd remote status\`)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
338
|
+
3. Do not use \`sdd push --all\` unless the user explicitly asks for a full reseed
|
|
339
|
+
4. If pull reports conflicts, do not overwrite local files blindly; report conflicts and ask how to proceed
|
|
340
|
+
5. Do not edit files inside \`.sdd/\` manually
|
|
341
|
+
6. Keep status transitions explicit: enrich first, then \`sdd mark-drafts-enriched\`, then push
|
|
342
|
+
7. **Always commit before pushing** when the sync workflow makes code changes
|
|
327
343
|
|
|
328
344
|
## Related commands
|
|
329
345
|
|
|
@@ -332,6 +348,8 @@ sdd remote status
|
|
|
332
348
|
- \`sdd pull\`
|
|
333
349
|
- \`sdd drafts\`
|
|
334
350
|
- \`sdd mark-drafts-enriched\`
|
|
351
|
+
- \`sdd sync\`
|
|
352
|
+
- \`sdd mark-synced\`
|
|
335
353
|
- \`sdd push\`
|
|
336
354
|
`;
|
|
337
355
|
|