@bicorne/task-flow 0.1.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/LICENSE +21 -0
- package/README.md +252 -0
- package/SKILL.md +250 -0
- package/assets/.harnessrc +10 -0
- package/assets/PHASE-task.json.example +50 -0
- package/assets/design.md +69 -0
- package/assets/hooks.json +15 -0
- package/assets/product-requirement.md +82 -0
- package/assets/schema.json +127 -0
- package/assets/tasks.md +26 -0
- package/dist/commands/analyze.d.ts +32 -0
- package/dist/commands/analyze.js +338 -0
- package/dist/commands/archive.d.ts +11 -0
- package/dist/commands/archive.js +53 -0
- package/dist/commands/design.d.ts +38 -0
- package/dist/commands/design.js +492 -0
- package/dist/commands/extract.d.ts +31 -0
- package/dist/commands/extract.js +477 -0
- package/dist/commands/init.d.ts +24 -0
- package/dist/commands/init.js +165 -0
- package/dist/commands/merge/index.d.ts +17 -0
- package/dist/commands/merge/index.js +322 -0
- package/dist/commands/merge/merger.d.ts +18 -0
- package/dist/commands/merge/merger.js +151 -0
- package/dist/commands/merge/types.d.ts +67 -0
- package/dist/commands/merge/types.js +6 -0
- package/dist/commands/merge/validators.d.ts +14 -0
- package/dist/commands/merge/validators.js +147 -0
- package/dist/commands/merge.d.ts +7 -0
- package/dist/commands/merge.js +15 -0
- package/dist/commands/start.d.ts +32 -0
- package/dist/commands/start.js +265 -0
- package/dist/commands/status.d.ts +15 -0
- package/dist/commands/status.js +143 -0
- package/dist/commands/sync.d.ts +11 -0
- package/dist/commands/sync.js +58 -0
- package/dist/commands/tasks-gen/doc-parser.d.ts +7 -0
- package/dist/commands/tasks-gen/doc-parser.js +259 -0
- package/dist/commands/tasks-gen/generators.d.ts +33 -0
- package/dist/commands/tasks-gen/generators.js +141 -0
- package/dist/commands/tasks-gen/index.d.ts +30 -0
- package/dist/commands/tasks-gen/index.js +345 -0
- package/dist/commands/tasks-gen/parsers.d.ts +29 -0
- package/dist/commands/tasks-gen/parsers.js +272 -0
- package/dist/commands/tasks-gen/templates.d.ts +8 -0
- package/dist/commands/tasks-gen/templates.js +37 -0
- package/dist/commands/tasks-gen/types.d.ts +71 -0
- package/dist/commands/tasks-gen/types.js +17 -0
- package/dist/commands/tasks-gen/validators.d.ts +14 -0
- package/dist/commands/tasks-gen/validators.js +54 -0
- package/dist/commands/tasks.d.ts +9 -0
- package/dist/commands/tasks.js +22 -0
- package/dist/commands/worktree.d.ts +28 -0
- package/dist/commands/worktree.js +275 -0
- package/dist/hooks/check-prd-exists.d.ts +20 -0
- package/dist/hooks/check-prd-exists.js +61 -0
- package/dist/hooks/check-worktree-conflict.d.ts +34 -0
- package/dist/hooks/check-worktree-conflict.js +107 -0
- package/dist/hooks/hook-runner/executor.d.ts +18 -0
- package/dist/hooks/hook-runner/executor.js +143 -0
- package/dist/hooks/hook-runner/index.d.ts +64 -0
- package/dist/hooks/hook-runner/index.js +220 -0
- package/dist/hooks/hook-runner/loader.d.ts +23 -0
- package/dist/hooks/hook-runner/loader.js +126 -0
- package/dist/hooks/hook-runner/types.d.ts +59 -0
- package/dist/hooks/hook-runner/types.js +6 -0
- package/dist/hooks/hook-runner.d.ts +9 -0
- package/dist/hooks/hook-runner.js +30 -0
- package/dist/hooks/phase-complete-detector.d.ts +35 -0
- package/dist/hooks/phase-complete-detector.js +203 -0
- package/dist/hooks/phase-gate-validator.d.ts +76 -0
- package/dist/hooks/phase-gate-validator.js +407 -0
- package/dist/hooks/save-checkpoint.d.ts +43 -0
- package/dist/hooks/save-checkpoint.js +144 -0
- package/dist/hooks/start-mcp-servers.d.ts +50 -0
- package/dist/hooks/start-mcp-servers.js +75 -0
- package/dist/hooks/stop-mcp-servers.d.ts +40 -0
- package/dist/hooks/stop-mcp-servers.js +58 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +238 -0
- package/dist/lib/archive.d.ts +31 -0
- package/dist/lib/archive.js +226 -0
- package/dist/lib/config.d.ts +93 -0
- package/dist/lib/config.js +251 -0
- package/dist/lib/constants.d.ts +222 -0
- package/dist/lib/constants.js +247 -0
- package/dist/lib/interactive.d.ts +31 -0
- package/dist/lib/interactive.js +166 -0
- package/dist/lib/mcp-client.d.ts +156 -0
- package/dist/lib/mcp-client.js +370 -0
- package/dist/lib/state.d.ts +119 -0
- package/dist/lib/state.js +293 -0
- package/dist/slash/executor.d.ts +22 -0
- package/dist/slash/executor.js +259 -0
- package/dist/slash/index.d.ts +11 -0
- package/dist/slash/index.js +45 -0
- package/dist/slash/parser.d.ts +24 -0
- package/dist/slash/parser.js +101 -0
- package/dist/slash/registry.d.ts +22 -0
- package/dist/slash/registry.js +155 -0
- package/dist/spec/openspec-to-task/builders.d.ts +107 -0
- package/dist/spec/openspec-to-task/builders.js +138 -0
- package/dist/spec/openspec-to-task/index.d.ts +20 -0
- package/dist/spec/openspec-to-task/index.js +182 -0
- package/dist/spec/openspec-to-task/parsers.d.ts +65 -0
- package/dist/spec/openspec-to-task/parsers.js +232 -0
- package/dist/spec/openspec-to-task/types.d.ts +49 -0
- package/dist/spec/openspec-to-task/types.js +6 -0
- package/dist/spec/sync-openspec-to-task.d.ts +7 -0
- package/dist/spec/sync-openspec-to-task.js +21 -0
- package/dist/spec/sync-task-to-openspec.d.ts +27 -0
- package/dist/spec/sync-task-to-openspec.js +288 -0
- package/dist/types/ai-context.d.ts +108 -0
- package/dist/types/ai-context.js +9 -0
- package/package.json +66 -0
- package/references/AI-CONVERSATION-TUTORIAL.md +270 -0
- package/references/CLI-TUTORIAL.md +447 -0
- package/references/GIT-WORKTREE-SOP.md +109 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* spec/openspec-to-task/index.ts
|
|
4
|
+
* Sync Spec change to harness task definition
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node sync-openspec-to-task.js --change <change-name> [options]
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.syncSpecToTask = syncSpecToTask;
|
|
44
|
+
exports.main = main;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const config_1 = require("../../lib/config");
|
|
48
|
+
const parsers_1 = require("./parsers");
|
|
49
|
+
const builders_1 = require("./builders");
|
|
50
|
+
/**
|
|
51
|
+
* Validate config has required properties for sync
|
|
52
|
+
*/
|
|
53
|
+
function validateConfigForSync(config) {
|
|
54
|
+
if (!config.projectRoot) {
|
|
55
|
+
return {
|
|
56
|
+
valid: false,
|
|
57
|
+
reason: 'missing-project-root',
|
|
58
|
+
message: 'Config missing projectRoot',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (!config.specRootAbs) {
|
|
62
|
+
return {
|
|
63
|
+
valid: false,
|
|
64
|
+
reason: 'missing-spec-root',
|
|
65
|
+
message: 'Config missing specRootAbs',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (!config.tasksDirAbs) {
|
|
69
|
+
return {
|
|
70
|
+
valid: false,
|
|
71
|
+
reason: 'missing-tasks-dir',
|
|
72
|
+
message: 'Config missing tasksDirAbs',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { valid: true };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Sync Spec to task
|
|
79
|
+
*/
|
|
80
|
+
function syncSpecToTask(options = {}) {
|
|
81
|
+
const config = (0, config_1.loadConfig)({ cwd: options.cwd });
|
|
82
|
+
const changeName = String(options.change || '').trim();
|
|
83
|
+
if (!changeName) {
|
|
84
|
+
return {
|
|
85
|
+
success: false,
|
|
86
|
+
reason: 'missing-change',
|
|
87
|
+
message: 'Missing required argument: --change <change-name>',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// Validate config before use
|
|
91
|
+
const configValidation = validateConfigForSync(config);
|
|
92
|
+
if (!configValidation.valid) {
|
|
93
|
+
return {
|
|
94
|
+
success: false,
|
|
95
|
+
reason: configValidation.reason,
|
|
96
|
+
message: configValidation.message || 'Unknown config validation error',
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const projectRoot = config.projectRoot || process.cwd();
|
|
100
|
+
const specRoot = config.specRootAbs || path.resolve(projectRoot, 'spec');
|
|
101
|
+
const changeDir = path.resolve(specRoot, 'changes', changeName);
|
|
102
|
+
const proposalPath = path.resolve(changeDir, 'product-requirement.md');
|
|
103
|
+
const designPath = path.resolve(changeDir, 'design.md');
|
|
104
|
+
const tasksPath = path.resolve(changeDir, 'tasks.md');
|
|
105
|
+
if (!fs.existsSync(changeDir)) {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
reason: 'missing-change-dir',
|
|
109
|
+
message: `Spec change not found: ${changeDir}`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Continue with sync only if change directory exists
|
|
113
|
+
const proposalContent = (0, parsers_1.readIfExists)(proposalPath);
|
|
114
|
+
const designContent = (0, parsers_1.readIfExists)(designPath);
|
|
115
|
+
const tasksContent = (0, parsers_1.readIfExists)(tasksPath);
|
|
116
|
+
const { manifestPath, manifest, executionPath } = (0, parsers_1.resolveStructuredConfig)(changeDir, changeName);
|
|
117
|
+
const syncParams = (0, builders_1.buildSyncParams)(options, config, changeName, changeDir, proposalContent, designContent, tasksContent, manifest, manifestPath, executionPath);
|
|
118
|
+
const taskOutputPath = path.resolve(projectRoot, String(options.output || path.relative(projectRoot, path.resolve(config.tasksDirAbs, `${syncParams.taskId}.json`))));
|
|
119
|
+
const taskJson = (0, builders_1.buildTaskJson)({
|
|
120
|
+
...syncParams,
|
|
121
|
+
changeName,
|
|
122
|
+
manifestPath,
|
|
123
|
+
executionPath,
|
|
124
|
+
config,
|
|
125
|
+
});
|
|
126
|
+
if (options.dryRun) {
|
|
127
|
+
return {
|
|
128
|
+
success: true,
|
|
129
|
+
taskId: syncParams.taskId,
|
|
130
|
+
json: taskJson,
|
|
131
|
+
dryRun: true,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
fs.mkdirSync(path.dirname(taskOutputPath), { recursive: true });
|
|
135
|
+
if (!options.force && fs.existsSync(taskOutputPath)) {
|
|
136
|
+
return {
|
|
137
|
+
success: false,
|
|
138
|
+
reason: 'file-exists',
|
|
139
|
+
message: 'Target task file already exists. Use --force to overwrite.',
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
fs.writeFileSync(taskOutputPath, taskJson, 'utf8');
|
|
143
|
+
return {
|
|
144
|
+
success: true,
|
|
145
|
+
taskId: syncParams.taskId,
|
|
146
|
+
taskPath: (0, parsers_1.ensurePosix)(path.relative(config.projectRoot, taskOutputPath)),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Main entry point
|
|
151
|
+
*/
|
|
152
|
+
function main() {
|
|
153
|
+
const args = (0, parsers_1.parseArgs)(process.argv);
|
|
154
|
+
const result = syncSpecToTask({
|
|
155
|
+
cwd: args.cwd,
|
|
156
|
+
change: args.change,
|
|
157
|
+
taskId: args['task-id'],
|
|
158
|
+
type: args.type,
|
|
159
|
+
priority: args.priority,
|
|
160
|
+
worktree: args.worktree,
|
|
161
|
+
output: args.output,
|
|
162
|
+
force: args.force === true,
|
|
163
|
+
dryRun: args['dry-run'] === true,
|
|
164
|
+
});
|
|
165
|
+
if (!result.success) {
|
|
166
|
+
console.error(`[SPEC-SYNC] ${result.message}`);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
if (result.dryRun) {
|
|
170
|
+
const jsonOutput = result.json || '';
|
|
171
|
+
process.stdout.write(jsonOutput);
|
|
172
|
+
process.exit(0);
|
|
173
|
+
}
|
|
174
|
+
console.log(`[SPEC-SYNC] Generated task: ${result.taskPath}`);
|
|
175
|
+
process.exit(0);
|
|
176
|
+
}
|
|
177
|
+
if (require.main === module) {
|
|
178
|
+
main();
|
|
179
|
+
}
|
|
180
|
+
exports.default = {
|
|
181
|
+
syncSpecToTask,
|
|
182
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spec/openspec-to-task/parsers.ts
|
|
3
|
+
* Parsing and extraction utilities for Spec to task sync
|
|
4
|
+
*/
|
|
5
|
+
import { ManifestJson } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Parse CLI arguments
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseArgs(argv: string[]): Record<string, string | boolean>;
|
|
10
|
+
/**
|
|
11
|
+
* Ensure POSIX path
|
|
12
|
+
*/
|
|
13
|
+
export declare function ensurePosix(p: string | undefined): string;
|
|
14
|
+
/**
|
|
15
|
+
* Sanitize task ID
|
|
16
|
+
*/
|
|
17
|
+
export declare function toTaskId(input: string | undefined): string;
|
|
18
|
+
/**
|
|
19
|
+
* Generate worktree name
|
|
20
|
+
*/
|
|
21
|
+
export declare function toWorktreeName(type: string, taskId: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Read file if exists
|
|
24
|
+
*/
|
|
25
|
+
export declare function readIfExists(filePath: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Read JSON if exists
|
|
28
|
+
*/
|
|
29
|
+
export declare function readJsonIfExists<T>(filePath: string): T | null;
|
|
30
|
+
/**
|
|
31
|
+
* Deduplicate array
|
|
32
|
+
*/
|
|
33
|
+
export declare function uniqueArray(items: (string | undefined)[]): string[];
|
|
34
|
+
/**
|
|
35
|
+
* Normalize boolean
|
|
36
|
+
*/
|
|
37
|
+
export declare function normalizeBoolean(value: unknown, fallback: boolean): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Extract title from proposal
|
|
40
|
+
*/
|
|
41
|
+
export declare function extractTitle(changeName: string, proposalContent: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Extract checklist items from tasks.md
|
|
44
|
+
*/
|
|
45
|
+
export declare function extractChecklistItems(tasksContent: string): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Extract file candidates from text
|
|
48
|
+
*/
|
|
49
|
+
export declare function extractFileCandidates(texts: (string | null)[]): string[];
|
|
50
|
+
/**
|
|
51
|
+
* Infer implementation validations
|
|
52
|
+
*/
|
|
53
|
+
export declare function inferImplementationValidations(checklistItems: string[]): string[];
|
|
54
|
+
/**
|
|
55
|
+
* Infer review validations
|
|
56
|
+
*/
|
|
57
|
+
export declare function inferReviewValidations(checklistItems: string[]): string[];
|
|
58
|
+
/**
|
|
59
|
+
* Resolve structured config from change directory
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveStructuredConfig(changeDir: string, changeName: string): {
|
|
62
|
+
manifestPath: string;
|
|
63
|
+
manifest: ManifestJson;
|
|
64
|
+
executionPath: string;
|
|
65
|
+
};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* spec/openspec-to-task/parsers.ts
|
|
4
|
+
* Parsing and extraction utilities for Spec to task sync
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.parseArgs = parseArgs;
|
|
41
|
+
exports.ensurePosix = ensurePosix;
|
|
42
|
+
exports.toTaskId = toTaskId;
|
|
43
|
+
exports.toWorktreeName = toWorktreeName;
|
|
44
|
+
exports.readIfExists = readIfExists;
|
|
45
|
+
exports.readJsonIfExists = readJsonIfExists;
|
|
46
|
+
exports.uniqueArray = uniqueArray;
|
|
47
|
+
exports.normalizeBoolean = normalizeBoolean;
|
|
48
|
+
exports.extractTitle = extractTitle;
|
|
49
|
+
exports.extractChecklistItems = extractChecklistItems;
|
|
50
|
+
exports.extractFileCandidates = extractFileCandidates;
|
|
51
|
+
exports.inferImplementationValidations = inferImplementationValidations;
|
|
52
|
+
exports.inferReviewValidations = inferReviewValidations;
|
|
53
|
+
exports.resolveStructuredConfig = resolveStructuredConfig;
|
|
54
|
+
const fs = __importStar(require("fs"));
|
|
55
|
+
const path = __importStar(require("path"));
|
|
56
|
+
/**
|
|
57
|
+
* Parse CLI arguments
|
|
58
|
+
*/
|
|
59
|
+
function parseArgs(argv) {
|
|
60
|
+
const args = {};
|
|
61
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
62
|
+
const token = argv[i] || '';
|
|
63
|
+
if (!token.startsWith('--')) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const key = token.slice(2);
|
|
67
|
+
const next = argv[i + 1] || '';
|
|
68
|
+
if (!next || next.startsWith('--')) {
|
|
69
|
+
args[key] = true;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
args[key] = next;
|
|
73
|
+
i += 1;
|
|
74
|
+
}
|
|
75
|
+
return args;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Ensure POSIX path
|
|
79
|
+
*/
|
|
80
|
+
function ensurePosix(p) {
|
|
81
|
+
return String(p || '').replace(/\\/g, '/');
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Sanitize task ID
|
|
85
|
+
*/
|
|
86
|
+
function toTaskId(input) {
|
|
87
|
+
return String(input || '')
|
|
88
|
+
.trim()
|
|
89
|
+
.replace(/[^a-zA-Z0-9-_]/g, '-')
|
|
90
|
+
.replace(/-+/g, '-')
|
|
91
|
+
.replace(/^-|-$/g, '');
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Generate worktree name
|
|
95
|
+
*/
|
|
96
|
+
function toWorktreeName(type, taskId) {
|
|
97
|
+
return `harness-${type}-${taskId}`;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Read file if exists
|
|
101
|
+
*/
|
|
102
|
+
function readIfExists(filePath) {
|
|
103
|
+
if (!fs.existsSync(filePath)) {
|
|
104
|
+
return '';
|
|
105
|
+
}
|
|
106
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Read JSON if exists
|
|
110
|
+
*/
|
|
111
|
+
function readJsonIfExists(filePath) {
|
|
112
|
+
if (!fs.existsSync(filePath)) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
throw new Error(`Invalid JSON: ${ensurePosix(filePath)} (${error instanceof Error ? error.message : 'Unknown error'})`, { cause: error });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Deduplicate array
|
|
124
|
+
*/
|
|
125
|
+
function uniqueArray(items) {
|
|
126
|
+
return [...new Set((Array.isArray(items) ? items : []).filter(Boolean).map((item) => ensurePosix(String(item).trim())))];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Normalize boolean
|
|
130
|
+
*/
|
|
131
|
+
function normalizeBoolean(value, fallback) {
|
|
132
|
+
if (typeof value === 'boolean') {
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
if (typeof value === 'string') {
|
|
136
|
+
if (value === 'true') {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
if (value === 'false') {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return fallback;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Extract title from proposal
|
|
147
|
+
*/
|
|
148
|
+
function extractTitle(changeName, proposalContent) {
|
|
149
|
+
const lines = proposalContent.split(/\r?\n/);
|
|
150
|
+
const heading = lines.find((line) => /^#\s+/.test(line));
|
|
151
|
+
if (!heading) {
|
|
152
|
+
return `Spec 变更:${changeName}`;
|
|
153
|
+
}
|
|
154
|
+
return heading.replace(/^#\s+/, '').trim();
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Extract checklist items from tasks.md
|
|
158
|
+
*/
|
|
159
|
+
function extractChecklistItems(tasksContent) {
|
|
160
|
+
const lines = tasksContent.split(/\r?\n/);
|
|
161
|
+
const items = [];
|
|
162
|
+
for (const line of lines) {
|
|
163
|
+
const match = line.match(/^\s*[-*]\s+\[(?: |x|X)\]\s+(.+)\s*$/);
|
|
164
|
+
if (match) {
|
|
165
|
+
items.push(match?.[1]?.trim() || '');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return items;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Extract file candidates from text
|
|
172
|
+
*/
|
|
173
|
+
function extractFileCandidates(texts) {
|
|
174
|
+
const merged = texts.filter(Boolean).join('\n');
|
|
175
|
+
const regex = /(?:^|[\s(])((?:server|web|docs|scripts|spec|\.harness|packages)\/[^\s):`'"]+\.[a-zA-Z0-9]+)(?=$|[\s),`'"])/gm;
|
|
176
|
+
const result = [];
|
|
177
|
+
let match = regex.exec(merged);
|
|
178
|
+
while (match) {
|
|
179
|
+
const file = ensurePosix(match?.[1]?.trim() || '');
|
|
180
|
+
if (file && !result.includes(file)) {
|
|
181
|
+
result.push(file);
|
|
182
|
+
}
|
|
183
|
+
match = regex.exec(merged);
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Infer implementation validations
|
|
189
|
+
*/
|
|
190
|
+
function inferImplementationValidations(checklistItems) {
|
|
191
|
+
const source = checklistItems.join('\n').toLowerCase();
|
|
192
|
+
const values = [];
|
|
193
|
+
if (/build|构建/.test(source)) {
|
|
194
|
+
values.push('build-passes');
|
|
195
|
+
}
|
|
196
|
+
if (/test|测试|回归/.test(source)) {
|
|
197
|
+
values.push('tests-pass');
|
|
198
|
+
}
|
|
199
|
+
if (/type|类型/.test(source)) {
|
|
200
|
+
values.push('typecheck-passes');
|
|
201
|
+
}
|
|
202
|
+
if (/lint|格式|规范/.test(source)) {
|
|
203
|
+
values.push('lint-passes');
|
|
204
|
+
}
|
|
205
|
+
if (values.length === 0) {
|
|
206
|
+
values.push('build-passes', 'tests-pass', 'typecheck-passes', 'lint-passes');
|
|
207
|
+
}
|
|
208
|
+
return values;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Infer review validations
|
|
212
|
+
*/
|
|
213
|
+
function inferReviewValidations(checklistItems) {
|
|
214
|
+
const source = checklistItems.join('\n').toLowerCase();
|
|
215
|
+
const values = ['security-check', 'review-conclusion'];
|
|
216
|
+
if (/cover|覆盖率/.test(source)) {
|
|
217
|
+
values.splice(1, 0, 'coverage-ge-80');
|
|
218
|
+
}
|
|
219
|
+
return values;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Resolve structured config from change directory
|
|
223
|
+
*/
|
|
224
|
+
function resolveStructuredConfig(changeDir, changeName) {
|
|
225
|
+
const manifestPath = path.resolve(changeDir, 'manifest.json');
|
|
226
|
+
const manifest = readJsonIfExists(manifestPath) || {};
|
|
227
|
+
return {
|
|
228
|
+
manifestPath: ensurePosix(path.relative(process.cwd(), manifestPath)),
|
|
229
|
+
manifest,
|
|
230
|
+
executionPath: ensurePosix(manifest.execution || `spec/changes/${changeName}/execution.json`),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spec/openspec-to-task/types.ts
|
|
3
|
+
* Type definitions for Spec to task sync
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Sync options
|
|
7
|
+
*/
|
|
8
|
+
export interface SyncOptions {
|
|
9
|
+
cwd?: string;
|
|
10
|
+
change?: string;
|
|
11
|
+
taskId?: string;
|
|
12
|
+
type?: string;
|
|
13
|
+
priority?: string;
|
|
14
|
+
worktree?: string;
|
|
15
|
+
output?: string;
|
|
16
|
+
force?: boolean;
|
|
17
|
+
dryRun?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Sync result
|
|
21
|
+
*/
|
|
22
|
+
export interface SyncResult {
|
|
23
|
+
success: boolean;
|
|
24
|
+
reason?: string;
|
|
25
|
+
message?: string;
|
|
26
|
+
taskId?: string;
|
|
27
|
+
taskPath?: string;
|
|
28
|
+
json?: string;
|
|
29
|
+
dryRun?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Manifest JSON structure
|
|
33
|
+
*/
|
|
34
|
+
export interface ManifestJson {
|
|
35
|
+
taskId?: string;
|
|
36
|
+
type?: string;
|
|
37
|
+
priority?: string;
|
|
38
|
+
title?: string;
|
|
39
|
+
files?: string[];
|
|
40
|
+
worktree?: string;
|
|
41
|
+
implementationValidation?: string[];
|
|
42
|
+
reviewValidation?: string[];
|
|
43
|
+
mergeStrategy?: {
|
|
44
|
+
type?: string;
|
|
45
|
+
deleteBranch?: boolean;
|
|
46
|
+
deleteWorktree?: boolean;
|
|
47
|
+
};
|
|
48
|
+
execution?: string;
|
|
49
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sync-openspec-to-task.ts
|
|
3
|
+
* Backward-compatible re-export from openspec-to-task/ module
|
|
4
|
+
* @deprecated Import from '../spec/openspec-to-task/index' directly
|
|
5
|
+
*/
|
|
6
|
+
export { syncSpecToTask, main, default } from './openspec-to-task/index';
|
|
7
|
+
export { SyncOptions, SyncResult, ManifestJson } from './openspec-to-task/types';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* sync-openspec-to-task.ts
|
|
4
|
+
* Backward-compatible re-export from openspec-to-task/ module
|
|
5
|
+
* @deprecated Import from '../spec/openspec-to-task/index' directly
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.default = exports.main = exports.syncSpecToTask = void 0;
|
|
12
|
+
var index_1 = require("./openspec-to-task/index");
|
|
13
|
+
Object.defineProperty(exports, "syncSpecToTask", { enumerable: true, get: function () { return index_1.syncSpecToTask; } });
|
|
14
|
+
Object.defineProperty(exports, "main", { enumerable: true, get: function () { return index_1.main; } });
|
|
15
|
+
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(index_1).default; } });
|
|
16
|
+
// Allow direct execution when invoked via spawnSync
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
18
|
+
const { main: runMain } = require('./openspec-to-task/index');
|
|
19
|
+
if (require.main === module) {
|
|
20
|
+
runMain();
|
|
21
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sync-task-to-openspec.ts
|
|
3
|
+
* Sync task execution status back to Spec
|
|
4
|
+
*/
|
|
5
|
+
export interface SyncOptions {
|
|
6
|
+
cwd?: string;
|
|
7
|
+
taskId?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface SyncResult {
|
|
10
|
+
skipped: boolean;
|
|
11
|
+
reason?: string;
|
|
12
|
+
taskId?: string;
|
|
13
|
+
change?: string;
|
|
14
|
+
executionPath?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Sync task to Spec
|
|
18
|
+
*/
|
|
19
|
+
export declare function syncTaskToSpec(options?: SyncOptions): SyncResult;
|
|
20
|
+
/**
|
|
21
|
+
* Main entry point
|
|
22
|
+
*/
|
|
23
|
+
export declare function main(): void;
|
|
24
|
+
declare const _default: {
|
|
25
|
+
syncTaskToSpec: typeof syncTaskToSpec;
|
|
26
|
+
};
|
|
27
|
+
export default _default;
|