@godv61/dsh-task-engine 0.28.0 → 0.29.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.
Files changed (49) hide show
  1. package/.adaptive-test.mjs +189 -0
  2. package/.hook-test.mjs +19 -2
  3. package/.workflow-test.mjs +6 -4
  4. package/README.md +22 -16
  5. package/docs/CHANGELOG.md +14 -1
  6. package/docs/README.md +6 -5
  7. package/docs/adaptive-workflows.md +72 -0
  8. package/docs/configuration.md +4 -4
  9. package/docs/faq.md +15 -7
  10. package/docs/getting-started.md +8 -5
  11. package/docs/manual-legacy.html +380 -0
  12. package/docs/manual.html +126 -380
  13. package/docs/roadmap.md +7 -8
  14. package/hooks/commit-msg +39 -22
  15. package/lib/adaptive.d.ts +15 -0
  16. package/lib/adaptive.js +54 -0
  17. package/lib/adaptive.js.map +1 -0
  18. package/lib/client.js +800 -433
  19. package/lib/client.js.map +3 -3
  20. package/lib/controller.d.ts +50 -0
  21. package/lib/controller.js +132 -0
  22. package/lib/controller.js.map +1 -1
  23. package/lib/dev-task.js +413 -18
  24. package/lib/dev-task.js.map +1 -1
  25. package/lib/engine.d.ts +20 -0
  26. package/lib/engine.js +4 -1
  27. package/lib/engine.js.map +1 -1
  28. package/lib/hook.js +50 -29
  29. package/lib/hook.js.map +1 -1
  30. package/lib/project-init.d.ts +23 -0
  31. package/lib/project-init.js +94 -0
  32. package/lib/project-init.js.map +1 -0
  33. package/lib/skill-audit.js +8 -7
  34. package/lib/skill-audit.js.map +1 -1
  35. package/lib/sonar.d.ts +33 -0
  36. package/lib/sonar.js +80 -0
  37. package/lib/sonar.js.map +1 -0
  38. package/package.json +5 -4
  39. package/preset/enable.mjs +2 -2
  40. package/preset/persona.md +4 -2
  41. package/scripts/verify-dsh-compat.mjs +14 -3
  42. package/scripts/verify-package.mjs +7 -3
  43. package/skills/architecture-design/SKILL.md +11 -0
  44. package/skills/code-development/SKILL.md +11 -0
  45. package/skills/code-review/SKILL.md +11 -0
  46. package/skills/eng-delivery/SKILL.md +6 -2
  47. package/skills/requirements-analysis/SKILL.md +11 -0
  48. package/skills/task-orchestration/SKILL.md +11 -0
  49. package/skills/test-validation/SKILL.md +11 -0
package/hooks/commit-msg CHANGED
@@ -148,7 +148,7 @@ function commitCheckpoint(state2, config2) {
148
148
  return { allowed: false, reason: "no commit is due at this stage under the current policy" };
149
149
  }
150
150
  function isEngineMeta(file) {
151
- return /^\.dsh\/(task-[^/]+\.json|eng\.json)$/.test(file);
151
+ return /^\.dsh\/(task-[^/]+\.json|eng\.json|meta\.json)$/.test(file);
152
152
  }
153
153
  function checkFileScope(state2, committing, config2) {
154
154
  if (!config2.commit.file_scope) return { ok: true, outside: [], noScope: false };
@@ -419,6 +419,9 @@ function normalizeTask(task) {
419
419
  branch: String(task.branch ?? ""),
420
420
  work_size: task.work_size ?? "standard",
421
421
  risk_level: task.risk_level ?? "standard",
422
+ ...task.complexity !== void 0 ? { complexity: task.complexity } : {},
423
+ ...task.sonar_policy !== void 0 ? { sonar_policy: task.sonar_policy } : {},
424
+ ...task.sonar_audit !== void 0 ? { sonar_audit: task.sonar_audit } : {},
422
425
  stage: String(task.stage ?? ""),
423
426
  requirement_confirmed: task.requirement_confirmed === true,
424
427
  solution_confirmed: task.solution_confirmed === true,
@@ -501,7 +504,21 @@ function extractTaskId(message2) {
501
504
  return match ? match[1] : void 0;
502
505
  }
503
506
  function isEngineMeta2(file) {
504
- return /^\.dsh\/(task-[^/]+\.json|eng\.json)$/.test(file);
507
+ return /^\.dsh\/(task-[^/]+\.json|eng\.json|meta\.json)$/.test(file);
508
+ }
509
+ function scopeHash(state2, root) {
510
+ const entries2 = [...new Set(state2.files)].filter((file) => !isEngineMeta2(file)).sort().map((file) => {
511
+ const target = import_node_path.default.resolve(root, file);
512
+ const inside = import_node_path.default.relative(root, target);
513
+ if (inside.startsWith("..") || import_node_path.default.isAbsolute(inside)) refuse(`\u4EFB\u52A1\u8303\u56F4\u8DEF\u5F84\u8D8A\u754C: ${file}`);
514
+ try {
515
+ return [file, hashText(import_node_fs.default.readFileSync(target, "utf8"))];
516
+ } catch (error) {
517
+ if (error.code === "ENOENT") return [file, null];
518
+ refuse(`\u65E0\u6CD5\u8BFB\u53D6\u4EFB\u52A1\u6587\u4EF6 ${file}: ${error instanceof Error ? error.message : String(error)}`);
519
+ }
520
+ });
521
+ return hashText(JSON.stringify(entries2));
505
522
  }
506
523
  function riskyPaths(entries2) {
507
524
  return entries2.filter((entry) => !isEngineMeta2(entry.path)).filter((entry) => DEFAULT_RISK_POLICY.sensitive_paths.some((prefix) => entry.path === prefix || entry.path.startsWith(`${prefix}/`))).map((entry) => entry.path);
@@ -536,30 +553,11 @@ function loadWorkflow(parsed) {
536
553
  }
537
554
  return resolved.config;
538
555
  }
539
- var cwd = process.cwd();
540
- var configPath = import_node_path.default.join(cwd, ".dsh", "eng.json");
541
- var config;
542
- if (import_node_fs.default.existsSync(configPath)) {
543
- let raw = "";
544
- try {
545
- raw = import_node_fs.default.readFileSync(configPath, "utf8");
546
- } catch {
547
- raw = "";
548
- }
549
- let parsed;
550
- try {
551
- parsed = JSON.parse(raw);
552
- } catch (error) {
553
- refuse("invalid JSON in .dsh/eng.json: " + (error instanceof Error ? error.message : String(error)));
554
- }
555
- config = loadWorkflow(parsed);
556
- } else {
557
- config = loadWorkflow(void 0);
558
- }
559
556
  if (!messageFile) {
560
557
  process.exit(0);
561
558
  }
562
559
  var message = readCommitMessage(messageFile);
560
+ var cwd = process.cwd();
563
561
  var tasks = loadTasks();
564
562
  var taskId = extractTaskId(message);
565
563
  var state;
@@ -578,8 +576,20 @@ if (!state) {
578
576
  if (state.flow?.hash !== void 0 && hashConfig(state.flow.config) !== state.flow.hash) {
579
577
  refuse(`\u4EFB\u52A1 ${state.id} \u7684\u6D41\u7A0B\u5FEB\u7167 hash \u4E0D\u5339\u914D\u2014\u2014task \u8BB0\u5F55\u5728\u521B\u5EFA\u540E\u88AB\u6539\u52A8\u8FC7\uFF0C\u4FEE\u590D\u6216\u91CD\u5EFA\u4EFB\u52A1\u540E\u518D\u63D0\u4EA4`);
580
578
  }
579
+ var config;
581
580
  if (state.flow !== void 0 && state.flow.config !== void 0) {
582
581
  config = state.flow.config;
582
+ } else {
583
+ const configPath = import_node_path.default.join(cwd, ".dsh", "eng.json");
584
+ if (import_node_fs.default.existsSync(configPath)) {
585
+ let parsed;
586
+ try {
587
+ parsed = JSON.parse(import_node_fs.default.readFileSync(configPath, "utf8"));
588
+ } catch (error) {
589
+ refuse("invalid JSON in .dsh/eng.json: " + (error instanceof Error ? error.message : String(error)));
590
+ }
591
+ config = loadWorkflow(parsed);
592
+ } else config = loadWorkflow(void 0);
583
593
  }
584
594
  var verdict = validateCommitMessage(message, config);
585
595
  if (!verdict.ok) {
@@ -589,6 +599,13 @@ var checkpoint = commitCheckpoint(state, config);
589
599
  if (!checkpoint.allowed) {
590
600
  refuse((checkpoint.reason ?? "commit checkpoint rejected") + "\uFF08\u5F53\u524D\u9636\u6BB5: " + state.stage + "\uFF09");
591
601
  }
602
+ if (state.sonar_policy?.enabled && ["\u4EE3\u7801\u5BA1\u6838", "\u5B8C\u6210"].includes(state.stage)) {
603
+ const audit = state.sonar_audit;
604
+ const lastCommit = state.commits.findLast((entry) => entry.hash !== void 0)?.hash;
605
+ if (!audit || audit.gate !== "OK" || audit.blocking.length > 0 || audit.scope_hash !== scopeHash(state, cwd) || !lastCommit || audit.commit_hash !== lastCommit) {
606
+ refuse("SonarQube \u5BA1\u6838\u672A\u901A\u8FC7\u6216\u5DF2\u8FC7\u671F\u2014\u2014\u5728\u4EE3\u7801\u5BA1\u6838\u9636\u6BB5\u5B8C\u6210\u65B0\u4E00\u6B21 CI \u626B\u63CF\u5E76\u8C03\u7528 dev_task operation=sonar_check");
607
+ }
608
+ }
592
609
  var entries = stagedEntries();
593
610
  var scope = checkFileScope(state, entries.map((entry) => entry.path), config);
594
611
  if (!scope.ok) {
@@ -0,0 +1,15 @@
1
+ /** Task-scoped engineering flows. The grade selects gates; risk remains separate. */
2
+ import type { WorkflowConfig } from './engine.ts';
3
+ export type Complexity = 'low' | 'medium' | 'high' | 'ultra';
4
+ export type MetaSkill = 'requirements-analysis' | 'architecture-design' | 'task-orchestration' | 'code-development' | 'test-validation' | 'code-review';
5
+ export declare const COMPLEXITY_OPTIONS: readonly {
6
+ id: Complexity;
7
+ label: string;
8
+ guidance: string;
9
+ }[];
10
+ export declare const META_STAGES: Readonly<Record<MetaSkill, string>>;
11
+ export declare const ADAPTIVE_VERSION = 1;
12
+ /** A grade is a per-task snapshot, never a project-wide forced flow. */
13
+ export declare function adaptiveWorkflow(grade: Complexity): WorkflowConfig;
14
+ export declare function isComplexity(value: unknown): value is Complexity;
15
+ export declare function metaForStage(stage: string): MetaSkill | undefined;
@@ -0,0 +1,54 @@
1
+ export const COMPLEXITY_OPTIONS = [
2
+ { id: 'low', label: '低', guidance: '边界明确的局部修改;一个实施项即可说明改动。' },
3
+ { id: 'medium', label: '中', guidance: '常规功能或缺陷修复;先写清需求与验收条件。' },
4
+ { id: 'high', label: '高', guidance: '跨模块或有先后依赖的需求;按实现顺序编排任务与交接产物。' },
5
+ { id: 'ultra', label: '超高', guidance: '完整新模块或大范围重构;先明确架构边界,再按依赖顺序实施。' },
6
+ ];
7
+ export const META_STAGES = {
8
+ 'requirements-analysis': '需求分析',
9
+ 'architecture-design': '架构设计',
10
+ 'task-orchestration': '任务编排',
11
+ 'code-development': '代码开发',
12
+ 'test-validation': '测试',
13
+ 'code-review': '代码审核',
14
+ };
15
+ export const ADAPTIVE_VERSION = 1;
16
+ const COMMON_ARTIFACTS = [
17
+ { stage: '需求分析', id: 'requirement', name: '需求与验收条件', fields: ['goal', 'scope', 'acceptance'] },
18
+ { stage: '架构设计', id: 'architecture', name: '架构边界', fields: ['approach', 'impact', 'tradeoffs'] },
19
+ { stage: '任务编排', id: 'task-plan', name: '按实现顺序排列的任务', fields: ['steps', 'dependencies', 'handoffs'] },
20
+ ];
21
+ /** A grade is a per-task snapshot, never a project-wide forced flow. */
22
+ export function adaptiveWorkflow(grade) {
23
+ const prefix = grade === 'low' ? []
24
+ : grade === 'medium' ? ['需求分析']
25
+ : grade === 'high' ? ['需求分析', '任务编排']
26
+ : ['需求分析', '架构设计', '任务编排'];
27
+ const stages = [...prefix, '代码开发', '测试', '代码审核', '完成'];
28
+ const transitions = stages.slice(0, -1).map((from, index) => {
29
+ const to = stages[index + 1];
30
+ const requires = from === '需求分析' || from === '架构设计' || from === '任务编排'
31
+ ? ['artifacts_present']
32
+ : from === '代码开发' ? ['todos_done']
33
+ : from === '测试' ? ['verified']
34
+ : from === '代码审核' ? ['review_passed'] : [];
35
+ return { from, to, requires };
36
+ });
37
+ return {
38
+ stages,
39
+ start_stage: stages[0],
40
+ transitions,
41
+ artifacts: COMMON_ARTIFACTS.filter(artifact => stages.includes(artifact.stage)),
42
+ // The branch must be committed/pushed before its CI Sonar scan can run.
43
+ commit: { policy: 'task', message_pattern: '', message_hint: '无格式要求', checkpoints: ['测试'], file_scope: true },
44
+ high_risk_requires_verification: true,
45
+ review_depth: grade === 'low' ? 'single' : 'two-stage',
46
+ };
47
+ }
48
+ export function isComplexity(value) {
49
+ return value === 'low' || value === 'medium' || value === 'high' || value === 'ultra';
50
+ }
51
+ export function metaForStage(stage) {
52
+ return Object.entries(META_STAGES).find(([, label]) => label === stage)?.[0];
53
+ }
54
+ //# sourceMappingURL=adaptive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adaptive.js","sourceRoot":"","sources":["../src/adaptive.ts"],"names":[],"mappings":"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"}