ai-spec-dev 0.58.0 → 0.60.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/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  <p align="center">
13
13
  <a href="https://github.com/hzhongzhong/ai-spec"><img src="https://img.shields.io/badge/GitHub-ai--spec-181717?logo=github" alt="GitHub" /></a>
14
14
  <a href="https://www.npmjs.com/package/ai-spec-dev"><img src="https://img.shields.io/npm/v/ai-spec-dev?color=cb3837&logo=npm" alt="npm" /></a>
15
- <img src="https://img.shields.io/badge/version-0.58.0-blue" alt="version" />
15
+ <img src="https://img.shields.io/badge/version-0.60.0-blue" alt="version" />
16
16
  <img src="https://img.shields.io/badge/tests-913%20passed-brightgreen" alt="tests" />
17
17
  <img src="https://img.shields.io/badge/providers-9-orange" alt="providers" />
18
18
  <img src="https://img.shields.io/badge/license-MIT-green" alt="license" />
@@ -159,7 +159,7 @@ export async function runSingleRepoPipeline(
159
159
  runLogger.setPromptHash(promptHash);
160
160
 
161
161
  // ── Step 1: Context ─────────────────────────────────────────────────────
162
- console.log(chalk.blue("[1/6] Loading project context..."));
162
+ console.log(chalk.bold("[1/10] Loading project context..."));
163
163
  runLogger.stageStart("context_load");
164
164
  const loader = new ContextLoader(currentDir);
165
165
  const context = await loader.loadProjectContext();
@@ -211,7 +211,7 @@ export async function runSingleRepoPipeline(
211
211
  }
212
212
 
213
213
  // ── Step 2: Spec + Tasks Generation (single AI call) ───────────────────
214
- console.log(chalk.blue(`\n[2/6] Generating spec with ${specProviderName}/${specModelName}...`));
214
+ console.log(chalk.bold(`\n[2/10] Generating spec with ${specProviderName}/${specModelName}...`));
215
215
  let specProvider: AIProvider = vcrReplayProvider ?? createProvider(specProviderName, specApiKey, specModelName);
216
216
  if (!vcrReplayProvider && opts.vcrRecord) {
217
217
  specVcrRecorder = new VcrRecordingProvider(specProvider);
@@ -251,10 +251,10 @@ export async function runSingleRepoPipeline(
251
251
  // ── Step 3: Interactive Refinement ──────────────────────────────────────
252
252
  let finalSpec: string;
253
253
  if (opts.fast) {
254
- console.log(chalk.gray("\n[3/6] Skipping refinement (--fast)."));
254
+ console.log(chalk.gray("\n[3/10] Skipping refinement (--fast)."));
255
255
  finalSpec = initialSpec;
256
256
  } else {
257
- console.log(chalk.blue("\n[3/6] Interactive spec refinement..."));
257
+ console.log(chalk.bold("\n[3/10] Interactive spec refinement..."));
258
258
  runLogger.stageStart("spec_refine");
259
259
  const refiner = new SpecRefiner(specProvider);
260
260
  finalSpec = await refiner.refineLoop(initialSpec);
@@ -269,7 +269,7 @@ export async function runSingleRepoPipeline(
269
269
 
270
270
  if (shouldRunAssessment) {
271
271
  if (!opts.auto) {
272
- console.log(chalk.blue("\n[3.4/6] Spec quality assessment..."));
272
+ console.log(chalk.bold("\n[3.4/10] Spec quality assessment..."));
273
273
  }
274
274
  runLogger.stageStart("spec_assess");
275
275
  const assessSpinner = startStage("spec_assess", "Evaluating spec quality...");
@@ -304,7 +304,7 @@ export async function runSingleRepoPipeline(
304
304
 
305
305
  // ── Step 3.5: Approval Gate ─────────────────────────────────────────────
306
306
  if (!opts.auto) {
307
- console.log(chalk.blue("\n[3.5/6] Approval Gate — review before code generation"));
307
+ console.log(chalk.bold("\n[Gate] Approval Gate — review before code generation"));
308
308
 
309
309
  const specLines = finalSpec.split("\n").length;
310
310
  const specWords = finalSpec.split(/\s+/).length;
@@ -366,7 +366,7 @@ export async function runSingleRepoPipeline(
366
366
 
367
367
  console.log(chalk.green(" ✔ Approved — continuing to code generation."));
368
368
  } else {
369
- console.log(chalk.gray("[3.5/6] Approval Gate: skipped (--auto)."));
369
+ console.log(chalk.gray("[Gate] Approval Gate: skipped (--auto)."));
370
370
  }
371
371
 
372
372
  // ── Step 3.8: DSL Extraction + Validation ──────────────────────────────
@@ -375,7 +375,7 @@ export async function runSingleRepoPipeline(
375
375
  if (opts.skipDsl) {
376
376
  console.log(chalk.gray("\n[DSL] Skipped (--skip-dsl)."));
377
377
  } else {
378
- console.log(chalk.blue("\n[DSL] Extracting structured DSL from spec..."));
378
+ console.log(chalk.bold("\n[DSL] Extracting structured DSL from spec..."));
379
379
  console.log(chalk.gray(` Provider: ${specProviderName}/${specModelName}`));
380
380
  runLogger.stageStart("dsl_extract");
381
381
  const dslSpinner = startStage("dsl_extract", "Extracting DSL from spec...");
@@ -480,7 +480,7 @@ export async function runSingleRepoPipeline(
480
480
 
481
481
  let workingDir = currentDir;
482
482
  if (!skipWorktree) {
483
- console.log(chalk.blue("\n[4/6] Setting up git worktree..."));
483
+ console.log(chalk.bold("\n[Git] Setting up git worktree..."));
484
484
  const worktreeManager = new GitWorktreeManager(currentDir);
485
485
  const worktreePath = await worktreeManager.createWorktree(idea);
486
486
  if (worktreePath) workingDir = worktreePath;
@@ -490,7 +490,7 @@ export async function runSingleRepoPipeline(
490
490
  : isFrontendProject
491
491
  ? " (frontend project — use --worktree to override)"
492
492
  : " (--skip-worktree)";
493
- console.log(chalk.gray(`[4/6] Skipping worktree${reason}.`));
493
+ console.log(chalk.gray(`[Git] Skipping worktree${reason}.`));
494
494
  }
495
495
 
496
496
  // ── Step 5: Save Spec (versioned) + Generate Tasks ──────────────────────
@@ -499,7 +499,7 @@ export async function runSingleRepoPipeline(
499
499
 
500
500
  const { filePath: specFile, version: specVersion } = await nextVersionPath(specsDir, featureSlug);
501
501
  await fs.writeFile(specFile, finalSpec, "utf-8");
502
- console.log(chalk.green(`\n[5/6] ✔ Spec saved: ${specFile}`) + chalk.gray(` (v${specVersion})`));
502
+ console.log(chalk.green(`\n ✔ Spec saved: ${specFile}`) + chalk.gray(` (v${specVersion})`));
503
503
 
504
504
  let savedDslFile: string | null = null;
505
505
  if (extractedDsl) {
@@ -550,7 +550,7 @@ export async function runSingleRepoPipeline(
550
550
  }
551
551
 
552
552
  // ── Step 6: Code Generation ─────────────────────────────────────────────
553
- console.log(chalk.blue(`\n[6/6] Code generation (mode: ${codegenMode})...`));
553
+ console.log(chalk.bold(`\n[6/10] Code generation (mode: ${codegenMode})...`));
554
554
  const rawCodegenProvider: AIProvider =
555
555
  codegenProviderName === specProviderName && codegenApiKey === specApiKey
556
556
  ? specProvider
@@ -646,13 +646,13 @@ export async function runSingleRepoPipeline(
646
646
 
647
647
  // ── Step 7: Test Skeleton Generation ───────────────────────────────────
648
648
  if (opts.tdd) {
649
- console.log(chalk.gray("\n[7/9] TDD mode — test files already written pre-implementation."));
649
+ console.log(chalk.gray("\n[7/10] TDD mode — test files already written pre-implementation."));
650
650
  } else if (opts.skipTests) {
651
- console.log(chalk.gray("\n[7/9] Skipping test generation (--skip-tests)."));
651
+ console.log(chalk.gray("\n[7/10] Skipping test generation (--skip-tests)."));
652
652
  } else if (!extractedDsl) {
653
- console.log(chalk.gray("\n[7/9] Skipping test generation (no DSL available)."));
653
+ console.log(chalk.gray("\n[7/10] Skipping test generation (no DSL available)."));
654
654
  } else {
655
- console.log(chalk.blue(`\n[7/9] Test skeleton generation...`));
655
+ console.log(chalk.bold(`\n[7/10] Test skeleton generation...`));
656
656
  runLogger.stageStart("test_gen");
657
657
  const testGen = new TestGenerator(codegenProvider);
658
658
  generatedTestFiles = await testGen.generate(extractedDsl, workingDir);
@@ -662,11 +662,11 @@ export async function runSingleRepoPipeline(
662
662
  // ── Step 8: Error Feedback Loop ─────────────────────────────────────────
663
663
  let compilePassed = false;
664
664
  if (opts.skipErrorFeedback) {
665
- console.log(chalk.gray("[8/9] Skipping error feedback (--skip-error-feedback)."));
665
+ console.log(chalk.gray("[8/10] Skipping error feedback (--skip-error-feedback)."));
666
666
  compilePassed = true;
667
667
  } else {
668
668
  if (opts.tdd) {
669
- console.log(chalk.cyan("[8/9] TDD mode — error feedback loop driving implementation to pass tests..."));
669
+ console.log(chalk.cyan("[8/10] TDD mode — error feedback loop driving implementation to pass tests..."));
670
670
  }
671
671
  runLogger.stageStart("error_feedback");
672
672
  const defaultCycles = opts.tdd ? 3 : 2;
@@ -682,7 +682,7 @@ export async function runSingleRepoPipeline(
682
682
  let reviewResult = "";
683
683
  let accumulatePromise: Promise<void> | undefined;
684
684
  if (!opts.skipReview) {
685
- console.log(chalk.blue("\n[9/9] Automated code review (3-pass: architecture + implementation + impact/complexity)..."));
685
+ console.log(chalk.bold("\n[9/10] Automated code review (3-pass: architecture + implementation + impact/complexity)..."));
686
686
  runLogger.stageStart("review");
687
687
  const reviewSpinner = startStage("review", "Running 3-pass code review...");
688
688
  const reviewer = new CodeReviewer(specProvider, workingDir);
@@ -789,6 +789,7 @@ export async function runSingleRepoPipeline(
789
789
  }
790
790
 
791
791
  // ── Step 10: Harness Self-Evaluation ────────────────────────────────────
792
+ console.log(chalk.bold("\n[10/10] Harness Self-Evaluation..."));
792
793
  runLogger.stageStart("self_eval");
793
794
  const selfEvalResult = runSelfEval({
794
795
  dsl: extractedDsl,
package/demo/demo.gif CHANGED
Binary file
package/demo/demo.tape CHANGED
@@ -10,43 +10,34 @@ Set Padding 24
10
10
  Set FontFamily "JetBrains Mono"
11
11
  Set PlaybackSpeed 1.0
12
12
 
13
- # ── Opening ────────────────────────────────────────────────────────────────────
13
+ # ── Scene 1: help ──────────────────────────────────────────────────────────────
14
14
  Sleep 500ms
15
15
  Type "ai-spec --help"
16
- Sleep 300ms
17
16
  Enter
18
- Sleep 3s
17
+ Sleep 5s
19
18
 
20
19
  # ── Scene 2: single-repo create pipeline ──────────────────────────────────────
21
- Sleep 800ms
22
- Type "ai-spec create 'Add task management feature to Vue admin'"
23
- Sleep 300ms
24
- Enter
25
- Sleep 300ms
20
+ Sleep 500ms
26
21
  Type "bash demo.sh create"
27
22
  Enter
28
- Sleep 40s
23
+ Sleep 50s
29
24
 
30
25
  # ── Scene 3: multi-repo workspace ─────────────────────────────────────────────
31
- Sleep 800ms
32
- Type "ai-spec create 'Add user profile sync' --workspace"
33
- Sleep 300ms
34
- Enter
35
- Sleep 300ms
26
+ Sleep 500ms
36
27
  Type "bash demo.sh multirepo"
37
28
  Enter
38
- Sleep 14s
29
+ Sleep 20s
39
30
 
40
31
  # ── Scene 4: DSL artifacts ─────────────────────────────────────────────────────
41
- Sleep 800ms
32
+ Sleep 500ms
42
33
  Type "bash demo.sh artifacts"
43
34
  Enter
44
- Sleep 10s
35
+ Sleep 16s
45
36
 
46
37
  # ── Scene 5: observability ─────────────────────────────────────────────────────
47
- Sleep 800ms
38
+ Sleep 500ms
48
39
  Type "bash demo.sh observability"
49
40
  Enter
50
- Sleep 8s
41
+ Sleep 12s
51
42
 
52
43
  Sleep 2s
package/dist/cli/index.js CHANGED
@@ -715,7 +715,7 @@ var require_package = __commonJS({
715
715
  "package.json"(exports2, module2) {
716
716
  module2.exports = {
717
717
  name: "ai-spec-dev",
718
- version: "0.56.0",
718
+ version: "0.59.0",
719
719
  description: "AI-driven Development Orchestrator SDK & CLI",
720
720
  main: "dist/index.js",
721
721
  types: "dist/index.d.ts",
@@ -13527,7 +13527,7 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
13527
13527
  setActiveLogger(runLogger);
13528
13528
  const promptHash = computePromptHash();
13529
13529
  runLogger.setPromptHash(promptHash);
13530
- console.log(import_chalk28.default.blue("[1/6] Loading project context..."));
13530
+ console.log(import_chalk28.default.bold("[1/10] Loading project context..."));
13531
13531
  runLogger.stageStart("context_load");
13532
13532
  const loader = new ContextLoader(currentDir);
13533
13533
  const context = await loader.loadProjectContext();
@@ -13575,8 +13575,8 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
13575
13575
  approach: choice.selectedApproach?.slice(0, 80)
13576
13576
  });
13577
13577
  }
13578
- console.log(import_chalk28.default.blue(`
13579
- [2/6] Generating spec with ${specProviderName}/${specModelName}...`));
13578
+ console.log(import_chalk28.default.bold(`
13579
+ [2/10] Generating spec with ${specProviderName}/${specModelName}...`));
13580
13580
  let specProvider = vcrReplayProvider ?? createProvider(specProviderName, specApiKey, specModelName);
13581
13581
  if (!vcrReplayProvider && opts.vcrRecord) {
13582
13582
  specVcrRecorder = new VcrRecordingProvider(specProvider);
@@ -13612,10 +13612,10 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
13612
13612
  }
13613
13613
  let finalSpec;
13614
13614
  if (opts.fast) {
13615
- console.log(import_chalk28.default.gray("\n[3/6] Skipping refinement (--fast)."));
13615
+ console.log(import_chalk28.default.gray("\n[3/10] Skipping refinement (--fast)."));
13616
13616
  finalSpec = initialSpec;
13617
13617
  } else {
13618
- console.log(import_chalk28.default.blue("\n[3/6] Interactive spec refinement..."));
13618
+ console.log(import_chalk28.default.bold("\n[3/10] Interactive spec refinement..."));
13619
13619
  runLogger.stageStart("spec_refine");
13620
13620
  const refiner = new SpecRefiner(specProvider);
13621
13621
  finalSpec = await refiner.refineLoop(initialSpec);
@@ -13626,7 +13626,7 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
13626
13626
  const shouldRunAssessment = !opts.skipAssessment && (!opts.auto || minScore > 0);
13627
13627
  if (shouldRunAssessment) {
13628
13628
  if (!opts.auto) {
13629
- console.log(import_chalk28.default.blue("\n[3.4/6] Spec quality assessment..."));
13629
+ console.log(import_chalk28.default.bold("\n[3.4/10] Spec quality assessment..."));
13630
13630
  }
13631
13631
  runLogger.stageStart("spec_assess");
13632
13632
  const assessSpinner = startStage("spec_assess", "Evaluating spec quality...");
@@ -13660,7 +13660,7 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
13660
13660
  }
13661
13661
  }
13662
13662
  if (!opts.auto) {
13663
- console.log(import_chalk28.default.blue("\n[3.5/6] Approval Gate \u2014 review before code generation"));
13663
+ console.log(import_chalk28.default.bold("\n[Gate] Approval Gate \u2014 review before code generation"));
13664
13664
  const specLines = finalSpec.split("\n").length;
13665
13665
  const specWords = finalSpec.split(/\s+/).length;
13666
13666
  const taskCountHint = initialTasks.length > 0 ? ` Tasks generated : ${initialTasks.length}` : "";
@@ -13714,13 +13714,13 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
13714
13714
  }
13715
13715
  console.log(import_chalk28.default.green(" \u2714 Approved \u2014 continuing to code generation."));
13716
13716
  } else {
13717
- console.log(import_chalk28.default.gray("[3.5/6] Approval Gate: skipped (--auto)."));
13717
+ console.log(import_chalk28.default.gray("[Gate] Approval Gate: skipped (--auto)."));
13718
13718
  }
13719
13719
  let extractedDsl = null;
13720
13720
  if (opts.skipDsl) {
13721
13721
  console.log(import_chalk28.default.gray("\n[DSL] Skipped (--skip-dsl)."));
13722
13722
  } else {
13723
- console.log(import_chalk28.default.blue("\n[DSL] Extracting structured DSL from spec..."));
13723
+ console.log(import_chalk28.default.bold("\n[DSL] Extracting structured DSL from spec..."));
13724
13724
  console.log(import_chalk28.default.gray(` Provider: ${specProviderName}/${specModelName}`));
13725
13725
  runLogger.stageStart("dsl_extract");
13726
13726
  const dslSpinner = startStage("dsl_extract", "Extracting DSL from spec...");
@@ -13812,20 +13812,20 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
13812
13812
  const skipWorktree = opts.worktree ? false : opts.skipWorktree || isFrontendProject2;
13813
13813
  let workingDir = currentDir;
13814
13814
  if (!skipWorktree) {
13815
- console.log(import_chalk28.default.blue("\n[4/6] Setting up git worktree..."));
13815
+ console.log(import_chalk28.default.bold("\n[Git] Setting up git worktree..."));
13816
13816
  const worktreeManager = new GitWorktreeManager(currentDir);
13817
13817
  const worktreePath = await worktreeManager.createWorktree(idea);
13818
13818
  if (worktreePath) workingDir = worktreePath;
13819
13819
  } else {
13820
13820
  const reason = opts.worktree ? "" : isFrontendProject2 ? " (frontend project \u2014 use --worktree to override)" : " (--skip-worktree)";
13821
- console.log(import_chalk28.default.gray(`[4/6] Skipping worktree${reason}.`));
13821
+ console.log(import_chalk28.default.gray(`[Git] Skipping worktree${reason}.`));
13822
13822
  }
13823
13823
  const specsDir = path30.join(workingDir, "specs");
13824
13824
  await fs31.ensureDir(specsDir);
13825
13825
  const { filePath: specFile, version: specVersion } = await nextVersionPath(specsDir, featureSlug);
13826
13826
  await fs31.writeFile(specFile, finalSpec, "utf-8");
13827
13827
  console.log(import_chalk28.default.green(`
13828
- [5/6] \u2714 Spec saved: ${specFile}`) + import_chalk28.default.gray(` (v${specVersion})`));
13828
+ \u2714 Spec saved: ${specFile}`) + import_chalk28.default.gray(` (v${specVersion})`));
13829
13829
  let savedDslFile = null;
13830
13830
  if (extractedDsl) {
13831
13831
  const dslExtractor = new DslExtractor(specProvider);
@@ -13869,8 +13869,8 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
13869
13869
  console.log(import_chalk28.default.yellow(" \u26A0 No tasks generated \u2014 code generation will use fallback file planning."));
13870
13870
  }
13871
13871
  }
13872
- console.log(import_chalk28.default.blue(`
13873
- [6/6] Code generation (mode: ${codegenMode})...`));
13872
+ console.log(import_chalk28.default.bold(`
13873
+ [6/10] Code generation (mode: ${codegenMode})...`));
13874
13874
  const rawCodegenProvider = codegenProviderName === specProviderName && codegenApiKey === specApiKey ? specProvider : createProvider(codegenProviderName, codegenApiKey, codegenModelName);
13875
13875
  let codegenProvider;
13876
13876
  if (!vcrReplayProvider && opts.vcrRecord && !(rawCodegenProvider instanceof VcrRecordingProvider)) {
@@ -13946,14 +13946,14 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
13946
13946
  }
13947
13947
  }
13948
13948
  if (opts.tdd) {
13949
- console.log(import_chalk28.default.gray("\n[7/9] TDD mode \u2014 test files already written pre-implementation."));
13949
+ console.log(import_chalk28.default.gray("\n[7/10] TDD mode \u2014 test files already written pre-implementation."));
13950
13950
  } else if (opts.skipTests) {
13951
- console.log(import_chalk28.default.gray("\n[7/9] Skipping test generation (--skip-tests)."));
13951
+ console.log(import_chalk28.default.gray("\n[7/10] Skipping test generation (--skip-tests)."));
13952
13952
  } else if (!extractedDsl) {
13953
- console.log(import_chalk28.default.gray("\n[7/9] Skipping test generation (no DSL available)."));
13953
+ console.log(import_chalk28.default.gray("\n[7/10] Skipping test generation (no DSL available)."));
13954
13954
  } else {
13955
- console.log(import_chalk28.default.blue(`
13956
- [7/9] Test skeleton generation...`));
13955
+ console.log(import_chalk28.default.bold(`
13956
+ [7/10] Test skeleton generation...`));
13957
13957
  runLogger.stageStart("test_gen");
13958
13958
  const testGen = new TestGenerator(codegenProvider);
13959
13959
  generatedTestFiles = await testGen.generate(extractedDsl, workingDir);
@@ -13961,11 +13961,11 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
13961
13961
  }
13962
13962
  let compilePassed = false;
13963
13963
  if (opts.skipErrorFeedback) {
13964
- console.log(import_chalk28.default.gray("[8/9] Skipping error feedback (--skip-error-feedback)."));
13964
+ console.log(import_chalk28.default.gray("[8/10] Skipping error feedback (--skip-error-feedback)."));
13965
13965
  compilePassed = true;
13966
13966
  } else {
13967
13967
  if (opts.tdd) {
13968
- console.log(import_chalk28.default.cyan("[8/9] TDD mode \u2014 error feedback loop driving implementation to pass tests..."));
13968
+ console.log(import_chalk28.default.cyan("[8/10] TDD mode \u2014 error feedback loop driving implementation to pass tests..."));
13969
13969
  }
13970
13970
  runLogger.stageStart("error_feedback");
13971
13971
  const defaultCycles = opts.tdd ? 3 : 2;
@@ -13979,7 +13979,7 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
13979
13979
  let reviewResult = "";
13980
13980
  let accumulatePromise;
13981
13981
  if (!opts.skipReview) {
13982
- console.log(import_chalk28.default.blue("\n[9/9] Automated code review (3-pass: architecture + implementation + impact/complexity)..."));
13982
+ console.log(import_chalk28.default.bold("\n[9/10] Automated code review (3-pass: architecture + implementation + impact/complexity)..."));
13983
13983
  runLogger.stageStart("review");
13984
13984
  const reviewSpinner = startStage("review", "Running 3-pass code review...");
13985
13985
  const reviewer = new CodeReviewer(specProvider, workingDir);
@@ -14065,6 +14065,7 @@ async function runSingleRepoPipeline(idea, opts, currentDir, config2) {
14065
14065
  }
14066
14066
  }
14067
14067
  }
14068
+ console.log(import_chalk28.default.bold("\n[10/10] Harness Self-Evaluation..."));
14068
14069
  runLogger.stageStart("self_eval");
14069
14070
  const selfEvalResult = runSelfEval({
14070
14071
  dsl: extractedDsl,