@mcp-consultant-tools/azure-devops 30.0.0-beta.9 → 31.0.0-beta.2

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 (129) hide show
  1. package/build/azure-devops-client.d.ts.map +1 -1
  2. package/build/azure-devops-client.js +23 -3
  3. package/build/azure-devops-client.js.map +1 -1
  4. package/build/cli/commands/index.d.ts +1 -0
  5. package/build/cli/commands/index.d.ts.map +1 -1
  6. package/build/cli/commands/index.js +3 -0
  7. package/build/cli/commands/index.js.map +1 -1
  8. package/build/cli/commands/test-commands.d.ts +7 -0
  9. package/build/cli/commands/test-commands.d.ts.map +1 -0
  10. package/build/cli/commands/test-commands.js +131 -0
  11. package/build/cli/commands/test-commands.js.map +1 -0
  12. package/build/cli/commands/work-item-commands.d.ts.map +1 -1
  13. package/build/cli/commands/work-item-commands.js +34 -0
  14. package/build/cli/commands/work-item-commands.js.map +1 -1
  15. package/build/context-factory.d.ts.map +1 -1
  16. package/build/context-factory.js +10 -4
  17. package/build/context-factory.js.map +1 -1
  18. package/build/schemas.d.ts +13 -0
  19. package/build/schemas.d.ts.map +1 -0
  20. package/build/schemas.js +47 -0
  21. package/build/schemas.js.map +1 -0
  22. package/build/services/checklist-service.d.ts.map +1 -1
  23. package/build/services/checklist-service.js +3 -0
  24. package/build/services/checklist-service.js.map +1 -1
  25. package/build/services/index.d.ts +1 -0
  26. package/build/services/index.d.ts.map +1 -1
  27. package/build/services/index.js +1 -0
  28. package/build/services/index.js.map +1 -1
  29. package/build/services/sync-service.d.ts.map +1 -1
  30. package/build/services/sync-service.js +75 -14
  31. package/build/services/sync-service.js.map +1 -1
  32. package/build/services/test-service.d.ts +106 -0
  33. package/build/services/test-service.d.ts.map +1 -0
  34. package/build/services/test-service.js +245 -0
  35. package/build/services/test-service.js.map +1 -0
  36. package/build/services/work-item-service.d.ts +29 -1
  37. package/build/services/work-item-service.d.ts.map +1 -1
  38. package/build/services/work-item-service.js +66 -6
  39. package/build/services/work-item-service.js.map +1 -1
  40. package/build/sync/annotation-parser.d.ts +52 -0
  41. package/build/sync/annotation-parser.d.ts.map +1 -0
  42. package/build/sync/annotation-parser.js +83 -0
  43. package/build/sync/annotation-parser.js.map +1 -0
  44. package/build/sync/field-aliases.d.ts +35 -0
  45. package/build/sync/field-aliases.d.ts.map +1 -0
  46. package/build/sync/field-aliases.js +76 -0
  47. package/build/sync/field-aliases.js.map +1 -0
  48. package/build/sync/html-converter.d.ts.map +1 -1
  49. package/build/sync/html-converter.js +12 -2
  50. package/build/sync/html-converter.js.map +1 -1
  51. package/build/sync/html-detection.d.ts +18 -65
  52. package/build/sync/html-detection.d.ts.map +1 -1
  53. package/build/sync/html-detection.js +72 -113
  54. package/build/sync/html-detection.js.map +1 -1
  55. package/build/sync/image-handler.d.ts +66 -0
  56. package/build/sync/image-handler.d.ts.map +1 -0
  57. package/build/sync/image-handler.js +135 -0
  58. package/build/sync/image-handler.js.map +1 -0
  59. package/build/sync/image-manifest.d.ts +66 -0
  60. package/build/sync/image-manifest.d.ts.map +1 -0
  61. package/build/sync/image-manifest.js +96 -0
  62. package/build/sync/image-manifest.js.map +1 -0
  63. package/build/sync/image-sync.d.ts +88 -0
  64. package/build/sync/image-sync.d.ts.map +1 -0
  65. package/build/sync/image-sync.js +274 -0
  66. package/build/sync/image-sync.js.map +1 -0
  67. package/build/sync/index.d.ts +7 -0
  68. package/build/sync/index.d.ts.map +1 -1
  69. package/build/sync/index.js +7 -0
  70. package/build/sync/index.js.map +1 -1
  71. package/build/sync/legacy-mappings.d.ts +37 -0
  72. package/build/sync/legacy-mappings.d.ts.map +1 -0
  73. package/build/sync/legacy-mappings.js +75 -0
  74. package/build/sync/legacy-mappings.js.map +1 -0
  75. package/build/sync/markdown-serializer.d.ts +54 -60
  76. package/build/sync/markdown-serializer.d.ts.map +1 -1
  77. package/build/sync/markdown-serializer.js +607 -545
  78. package/build/sync/markdown-serializer.js.map +1 -1
  79. package/build/sync/task-serializer.d.ts.map +1 -1
  80. package/build/sync/task-serializer.js +46 -8
  81. package/build/sync/task-serializer.js.map +1 -1
  82. package/build/sync/template-loader.d.ts +56 -0
  83. package/build/sync/template-loader.d.ts.map +1 -0
  84. package/build/sync/template-loader.js +138 -0
  85. package/build/sync/template-loader.js.map +1 -0
  86. package/build/sync/templates/bug.md +25 -0
  87. package/build/sync/templates/epic.md +23 -0
  88. package/build/sync/templates/feature.md +23 -0
  89. package/build/sync/templates/task.md +14 -0
  90. package/build/sync/templates/user-story.md +26 -0
  91. package/build/tool-examples.d.ts +20 -0
  92. package/build/tool-examples.d.ts.map +1 -1
  93. package/build/tool-examples.js +41 -0
  94. package/build/tool-examples.js.map +1 -1
  95. package/build/tools/build-tools.d.ts.map +1 -1
  96. package/build/tools/build-tools.js +7 -6
  97. package/build/tools/build-tools.js.map +1 -1
  98. package/build/tools/checklist-tools.d.ts.map +1 -1
  99. package/build/tools/checklist-tools.js +6 -5
  100. package/build/tools/checklist-tools.js.map +1 -1
  101. package/build/tools/index.d.ts +1 -0
  102. package/build/tools/index.d.ts.map +1 -1
  103. package/build/tools/index.js +5 -2
  104. package/build/tools/index.js.map +1 -1
  105. package/build/tools/pull-request-tools.d.ts.map +1 -1
  106. package/build/tools/pull-request-tools.js +15 -14
  107. package/build/tools/pull-request-tools.js.map +1 -1
  108. package/build/tools/sync-tools.d.ts.map +1 -1
  109. package/build/tools/sync-tools.js +9 -8
  110. package/build/tools/sync-tools.js.map +1 -1
  111. package/build/tools/test-tools.d.ts +3 -0
  112. package/build/tools/test-tools.d.ts.map +1 -0
  113. package/build/tools/test-tools.js +184 -0
  114. package/build/tools/test-tools.js.map +1 -0
  115. package/build/tools/variable-group-tools.d.ts.map +1 -1
  116. package/build/tools/variable-group-tools.js +2 -1
  117. package/build/tools/variable-group-tools.js.map +1 -1
  118. package/build/tools/visualize-tools.d.ts.map +1 -1
  119. package/build/tools/visualize-tools.js +2 -1
  120. package/build/tools/visualize-tools.js.map +1 -1
  121. package/build/tools/wiki-tools.d.ts.map +1 -1
  122. package/build/tools/wiki-tools.js +4 -3
  123. package/build/tools/wiki-tools.js.map +1 -1
  124. package/build/tools/work-item-tools.d.ts.map +1 -1
  125. package/build/tools/work-item-tools.js +42 -11
  126. package/build/tools/work-item-tools.js.map +1 -1
  127. package/build/types.d.ts +2 -0
  128. package/build/types.d.ts.map +1 -1
  129. package/package.json +3 -3
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Frontmatter key → ADO reference name resolver.
3
+ *
4
+ * Frontmatter in synced markdown files uses raw ADO refnames as keys
5
+ * (e.g. `System.Title`, `Custom.ConsultancyProcess`). A small alias table
6
+ * provides friendly shortcuts for the most common fields so human-written
7
+ * files stay readable.
8
+ *
9
+ * Resolution rules:
10
+ * 1. Reserved keys (sync metadata) are returned as-is with a null refname.
11
+ * 2. Alias → refname lookup (case-sensitive).
12
+ * 3. Unknown keys are passed through unchanged (treated as refnames).
13
+ */
14
+ /** Keys that are sync metadata, never sent to ADO as field updates. */
15
+ export declare const RESERVED_FRONTMATTER_KEYS: Set<string>;
16
+ /**
17
+ * Friendly aliases → ADO reference names. Add entries here when a field is
18
+ * common enough across clients to deserve a short key.
19
+ */
20
+ export declare const FIELD_ALIASES: Record<string, string>;
21
+ /**
22
+ * Resolve a frontmatter key to its ADO refname, or null if the key is reserved
23
+ * sync metadata.
24
+ */
25
+ export declare function resolveRefname(key: string): string | null;
26
+ /**
27
+ * When writing frontmatter from ADO data, pick the friendliest key for a given
28
+ * refname (alias if one exists, else the refname itself).
29
+ */
30
+ export declare function preferredKey(refname: string): string;
31
+ /**
32
+ * True when a frontmatter key is reserved sync metadata (not an ADO field).
33
+ */
34
+ export declare function isReservedKey(key: string): boolean;
35
+ //# sourceMappingURL=field-aliases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-aliases.d.ts","sourceRoot":"","sources":["../../src/sync/field-aliases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,aASpC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAehD,CAAC;AAWF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGzD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Frontmatter key → ADO reference name resolver.
3
+ *
4
+ * Frontmatter in synced markdown files uses raw ADO refnames as keys
5
+ * (e.g. `System.Title`, `Custom.ConsultancyProcess`). A small alias table
6
+ * provides friendly shortcuts for the most common fields so human-written
7
+ * files stay readable.
8
+ *
9
+ * Resolution rules:
10
+ * 1. Reserved keys (sync metadata) are returned as-is with a null refname.
11
+ * 2. Alias → refname lookup (case-sensitive).
12
+ * 3. Unknown keys are passed through unchanged (treated as refnames).
13
+ */
14
+ /** Keys that are sync metadata, never sent to ADO as field updates. */
15
+ export const RESERVED_FRONTMATTER_KEYS = new Set([
16
+ 'id',
17
+ 'type',
18
+ 'project',
19
+ 'parent',
20
+ 'parentTitle',
21
+ 'url',
22
+ 'lastSyncedRevision',
23
+ 'lastSyncedAt',
24
+ ]);
25
+ /**
26
+ * Friendly aliases → ADO reference names. Add entries here when a field is
27
+ * common enough across clients to deserve a short key.
28
+ */
29
+ export const FIELD_ALIASES = {
30
+ title: 'System.Title',
31
+ state: 'System.State',
32
+ assignedTo: 'System.AssignedTo',
33
+ areaPath: 'System.AreaPath',
34
+ iterationPath: 'System.IterationPath',
35
+ tags: 'System.Tags',
36
+ priority: 'Microsoft.VSTS.Common.Priority',
37
+ severity: 'Microsoft.VSTS.Common.Severity',
38
+ storyPoints: 'Microsoft.VSTS.Scheduling.StoryPoints',
39
+ remainingWork: 'Microsoft.VSTS.Scheduling.RemainingWork',
40
+ effort: 'Microsoft.VSTS.Scheduling.Effort',
41
+ originalEstimate: 'Microsoft.VSTS.Scheduling.OriginalEstimate',
42
+ completedWork: 'Microsoft.VSTS.Scheduling.CompletedWork',
43
+ moscow: 'Custom.MoSCoW',
44
+ };
45
+ /** Reverse lookup: refname → canonical alias (first alias that resolves back). */
46
+ const REFNAME_TO_ALIAS = (() => {
47
+ const map = {};
48
+ for (const [alias, refname] of Object.entries(FIELD_ALIASES)) {
49
+ if (!(refname in map))
50
+ map[refname] = alias;
51
+ }
52
+ return map;
53
+ })();
54
+ /**
55
+ * Resolve a frontmatter key to its ADO refname, or null if the key is reserved
56
+ * sync metadata.
57
+ */
58
+ export function resolveRefname(key) {
59
+ if (RESERVED_FRONTMATTER_KEYS.has(key))
60
+ return null;
61
+ return FIELD_ALIASES[key] ?? key;
62
+ }
63
+ /**
64
+ * When writing frontmatter from ADO data, pick the friendliest key for a given
65
+ * refname (alias if one exists, else the refname itself).
66
+ */
67
+ export function preferredKey(refname) {
68
+ return REFNAME_TO_ALIAS[refname] ?? refname;
69
+ }
70
+ /**
71
+ * True when a frontmatter key is reserved sync metadata (not an ADO field).
72
+ */
73
+ export function isReservedKey(key) {
74
+ return RESERVED_FRONTMATTER_KEYS.has(key);
75
+ }
76
+ //# sourceMappingURL=field-aliases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-aliases.js","sourceRoot":"","sources":["../../src/sync/field-aliases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,uEAAuE;AACvE,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAS;IACvD,IAAI;IACJ,MAAM;IACN,SAAS;IACT,QAAQ;IACR,aAAa;IACb,KAAK;IACL,oBAAoB;IACpB,cAAc;CACf,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,KAAK,EAAE,cAAc;IACrB,KAAK,EAAE,cAAc;IACrB,UAAU,EAAE,mBAAmB;IAC/B,QAAQ,EAAE,iBAAiB;IAC3B,aAAa,EAAE,sBAAsB;IACrC,IAAI,EAAE,aAAa;IACnB,QAAQ,EAAE,gCAAgC;IAC1C,QAAQ,EAAE,gCAAgC;IAC1C,WAAW,EAAE,uCAAuC;IACpD,aAAa,EAAE,yCAAyC;IACxD,MAAM,EAAE,kCAAkC;IAC1C,gBAAgB,EAAE,4CAA4C;IAC9D,aAAa,EAAE,yCAAyC;IACxD,MAAM,EAAE,eAAe;CACxB,CAAC;AAEF,kFAAkF;AAClF,MAAM,gBAAgB,GAA2B,CAAC,GAAG,EAAE;IACrD,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QAC7D,IAAI,CAAC,CAAC,OAAO,IAAI,GAAG,CAAC;YAAE,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;IAC9C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC,EAAE,CAAC;AAEL;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,yBAAyB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,OAAO,aAAa,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,OAAO,gBAAgB,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,OAAO,yBAAyB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC5C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"html-converter.d.ts","sourceRoot":"","sources":["../../src/sync/html-converter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2BH;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA0BnD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE;IAAE,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;CAAE,EACrG,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC,MAAM,EAAE,CAAC,CAiCnB"}
1
+ {"version":3,"file":"html-converter.d.ts","sourceRoot":"","sources":["../../src/sync/html-converter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmCH;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA0BnD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE;IAAE,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;CAAE,EACrG,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC,MAAM,EAAE,CAAC,CAiCnB"}
@@ -18,10 +18,20 @@ turndown.addRule('adoSpan', {
18
18
  filter: ['span'],
19
19
  replacement: (content) => content,
20
20
  });
21
- // Remove empty divs that ADO sometimes creates
21
+ // Remove empty divs that ADO sometimes creates.
22
+ // IMPORTANT: do NOT strip divs whose only "content" is a meaningful element
23
+ // like <img>, <iframe>, <video> etc. — those have no text content but are
24
+ // not visually empty and stripping them silently deletes embedded media.
22
25
  turndown.addRule('emptyDiv', {
23
26
  filter: (node) => {
24
- return node.nodeName === 'DIV' && !node.textContent?.trim();
27
+ if (node.nodeName !== 'DIV')
28
+ return false;
29
+ if (node.textContent?.trim())
30
+ return false;
31
+ if (node.querySelector?.('img, iframe, video, audio, picture, embed, object, svg, canvas')) {
32
+ return false;
33
+ }
34
+ return true;
25
35
  },
26
36
  replacement: () => '',
27
37
  });
@@ -1 +1 @@
1
- {"version":3,"file":"html-converter.js","sourceRoot":"","sources":["../../src/sync/html-converter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"html-converter.js","sourceRoot":"","sources":["../../src/sync/html-converter.ts"],"names":[],"mappings":"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"}
@@ -1,93 +1,46 @@
1
1
  /**
2
- * HTML Detection Utility
2
+ * HTML detection for ADO large-text fields.
3
3
  *
4
- * Detects whether ADO work item content is HTML (legacy) or Markdown format.
5
- * ADO supports both formats and we can only sync markdown fields.
6
- */
7
- /**
8
- * Additional custom fields that can be synced.
9
- * These are configurable via environment variables since field names may vary by organization.
4
+ * ADO supports both HTML and Markdown for long-text fields. Sync only
5
+ * works against markdown, so before pulling (or pushing) we auto-detect
6
+ * HTML and either convert it or report it as skipped.
10
7
  *
11
- * Default field names are based on typical ADO custom field patterns.
12
- * GUID-based fields (like pre/post deployment) may vary between organizations.
13
- */
14
- export interface AdditionalFieldConfig {
15
- howToTest: string;
16
- deploymentInformation: string;
17
- predeploymentSteps: string;
18
- postdeploymentSteps: string;
19
- }
20
- /**
21
- * Get additional field configuration from environment variables
22
- * Falls back to common default field names
23
- */
24
- export declare function getAdditionalFieldConfig(): AdditionalFieldConfig;
25
- /**
26
- * Human-readable names for additional fields (for display/logging)
27
- */
28
- export declare const ADDITIONAL_FIELD_DISPLAY_NAMES: Record<keyof AdditionalFieldConfig, string>;
29
- /**
30
- * Large text fields that support markdown format in Azure DevOps.
31
- * These are the standard ADO fields that can be set to markdown format.
8
+ * This module is intentionally generic it no longer maintains a fixed
9
+ * list of "additional" custom fields. Callers pass in the refname list
10
+ * they care about (usually derived from the loaded template).
32
11
  */
33
- export declare const LARGE_TEXT_FIELDS: readonly ["System.Description", "Microsoft.VSTS.Common.AcceptanceCriteria", "Microsoft.VSTS.TCM.ReproSteps"];
34
12
  /**
35
- * Get all large text fields including custom ones from config.
36
- * Used to determine which fields should have markdown format set.
13
+ * Standard ADO large-text fields with native markdown support.
37
14
  */
38
- export declare function getAllLargeTextFields(): string[];
15
+ export declare const STANDARD_LARGE_TEXT_FIELDS: readonly ["System.Description", "Microsoft.VSTS.Common.AcceptanceCriteria", "Microsoft.VSTS.TCM.ReproSteps"];
39
16
  /**
40
- * Check if content is HTML format (as opposed to markdown)
41
- *
42
- * @param content - The content string to check
43
- * @returns true if content appears to be HTML, false if markdown or empty
17
+ * Return the list of large-text fields to check for a work item. Combines
18
+ * the standard set with any caller-supplied custom refnames (from the
19
+ * loaded template).
44
20
  */
21
+ export declare function getAllLargeTextFields(customRefnames?: string[]): string[];
45
22
  export declare function isHtmlContent(content: string | null | undefined): boolean;
46
- /**
47
- * Field format result type
48
- */
49
23
  export type FieldFormat = 'markdown' | 'html' | 'not_present';
50
- /**
51
- * Result of checking additional field formats
52
- */
53
- export interface AdditionalFieldFormats {
54
- howToTest: FieldFormat;
55
- deploymentInformation: FieldFormat;
56
- predeploymentSteps: FieldFormat;
57
- postdeploymentSteps: FieldFormat;
58
- }
59
- /**
60
- * Full field format check result
61
- */
62
24
  export interface FieldFormatResult {
63
25
  description: 'markdown' | 'html';
64
26
  acceptanceCriteria: 'markdown' | 'html';
27
+ reproSteps: 'markdown' | 'html' | 'not_present';
65
28
  ready: boolean;
66
29
  warnings: string[];
67
- additionalFields: AdditionalFieldFormats;
30
+ /** Format for each custom body field, keyed by refname. */
31
+ additionalFields: Record<string, FieldFormat>;
68
32
  details: {
69
33
  descriptionLength: number;
70
34
  acceptanceCriteriaLength: number;
35
+ reproStepsLength: number;
71
36
  };
72
37
  }
73
- /**
74
- * Check field formats for a work item
75
- *
76
- * Primary fields (Description, Acceptance Criteria): If HTML, work item is not ready for sync.
77
- * Secondary fields (How to Test, Deployment fields): If HTML, warn but allow sync (skip those fields).
78
- *
79
- * @param workItem - The work item object from ADO API
80
- * @returns Object with field format information
81
- */
82
38
  export declare function checkFieldFormats(workItem: {
83
39
  fields?: {
84
40
  'System.Description'?: string;
85
41
  'Microsoft.VSTS.Common.AcceptanceCriteria'?: string;
86
42
  [key: string]: any;
87
43
  };
88
- }): FieldFormatResult;
89
- /**
90
- * Get human-readable conversion instructions
91
- */
44
+ }, additionalRefnames?: string[]): FieldFormatResult;
92
45
  export declare function getConversionInstructions(): string;
93
46
  //# sourceMappingURL=html-detection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"html-detection.d.ts","sourceRoot":"","sources":["../../src/sync/html-detection.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,qBAAqB,CAOhE;AAED;;GAEG;AACH,eAAO,MAAM,8BAA8B,EAAE,MAAM,CAAC,MAAM,qBAAqB,EAAE,MAAM,CAKtF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,8GAIpB,CAAC;AAEX;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAShD;AA8BD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAsBzE;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,aAAa,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,WAAW,CAAC;IACvB,qBAAqB,EAAE,WAAW,CAAC;IACnC,kBAAkB,EAAE,WAAW,CAAC;IAChC,mBAAmB,EAAE,WAAW,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,UAAU,GAAG,MAAM,CAAC;IACjC,kBAAkB,EAAE,UAAU,GAAG,MAAM,CAAC;IACxC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,EAAE,sBAAsB,CAAC;IACzC,OAAO,EAAE;QACP,iBAAiB,EAAE,MAAM,CAAC;QAC1B,wBAAwB,EAAE,MAAM,CAAC;KAClC,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE;IAC1C,MAAM,CAAC,EAAE;QACP,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,0CAA0C,CAAC,EAAE,MAAM,CAAC;QACpD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;CACH,GAAG,iBAAiB,CAmDpB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAUlD"}
1
+ {"version":3,"file":"html-detection.d.ts","sourceRoot":"","sources":["../../src/sync/html-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;GAEG;AACH,eAAO,MAAM,0BAA0B,8GAI7B,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,cAAc,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CAG7E;AAgCD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CASzE;AAMD,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,aAAa,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,UAAU,GAAG,MAAM,CAAC;IACjC,kBAAkB,EAAE,UAAU,GAAG,MAAM,CAAC;IACxC,UAAU,EAAE,UAAU,GAAG,MAAM,GAAG,aAAa,CAAC;IAChD,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9C,OAAO,EAAE;QACP,iBAAiB,EAAE,MAAM,CAAC;QAC1B,wBAAwB,EAAE,MAAM,CAAC;QACjC,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE;IACR,MAAM,CAAC,EAAE;QACP,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,0CAA0C,CAAC,EAAE,MAAM,CAAC;QACpD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;CACH,EACD,kBAAkB,GAAE,MAAM,EAAO,GAChC,iBAAiB,CAqDnB;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAUlD"}
@@ -1,160 +1,119 @@
1
1
  /**
2
- * HTML Detection Utility
2
+ * HTML detection for ADO large-text fields.
3
3
  *
4
- * Detects whether ADO work item content is HTML (legacy) or Markdown format.
5
- * ADO supports both formats and we can only sync markdown fields.
6
- */
7
- /**
8
- * Get additional field configuration from environment variables
9
- * Falls back to common default field names
10
- */
11
- export function getAdditionalFieldConfig() {
12
- return {
13
- howToTest: process.env.AZUREDEVOPS_SYNC_FIELD_HOW_TO_TEST || 'Custom.Howtotest',
14
- deploymentInformation: process.env.AZUREDEVOPS_SYNC_FIELD_DEPLOYMENT_INFO || 'Custom.Deploymentinformation',
15
- predeploymentSteps: process.env.AZUREDEVOPS_SYNC_FIELD_PREDEPLOY || 'Custom.7519d1bc-5305-4905-822b-2b380e61b154',
16
- postdeploymentSteps: process.env.AZUREDEVOPS_SYNC_FIELD_POSTDEPLOY || 'Custom.abd6763f-a242-4938-85ed-bda419e34e7e',
17
- };
18
- }
19
- /**
20
- * Human-readable names for additional fields (for display/logging)
4
+ * ADO supports both HTML and Markdown for long-text fields. Sync only
5
+ * works against markdown, so before pulling (or pushing) we auto-detect
6
+ * HTML and either convert it or report it as skipped.
7
+ *
8
+ * This module is intentionally generic it no longer maintains a fixed
9
+ * list of "additional" custom fields. Callers pass in the refname list
10
+ * they care about (usually derived from the loaded template).
21
11
  */
22
- export const ADDITIONAL_FIELD_DISPLAY_NAMES = {
23
- howToTest: 'How to Test',
24
- deploymentInformation: 'Deployment Information',
25
- predeploymentSteps: 'Predeployment Steps',
26
- postdeploymentSteps: 'Postdeployment Steps',
27
- };
28
12
  /**
29
- * Large text fields that support markdown format in Azure DevOps.
30
- * These are the standard ADO fields that can be set to markdown format.
13
+ * Standard ADO large-text fields with native markdown support.
31
14
  */
32
- export const LARGE_TEXT_FIELDS = [
15
+ export const STANDARD_LARGE_TEXT_FIELDS = [
33
16
  'System.Description',
34
17
  'Microsoft.VSTS.Common.AcceptanceCriteria',
35
18
  'Microsoft.VSTS.TCM.ReproSteps',
36
19
  ];
37
20
  /**
38
- * Get all large text fields including custom ones from config.
39
- * Used to determine which fields should have markdown format set.
21
+ * Return the list of large-text fields to check for a work item. Combines
22
+ * the standard set with any caller-supplied custom refnames (from the
23
+ * loaded template).
40
24
  */
41
- export function getAllLargeTextFields() {
42
- const config = getAdditionalFieldConfig();
43
- return [
44
- ...LARGE_TEXT_FIELDS,
45
- config.howToTest,
46
- config.deploymentInformation,
47
- config.predeploymentSteps,
48
- config.postdeploymentSteps,
49
- ];
25
+ export function getAllLargeTextFields(customRefnames = []) {
26
+ const set = new Set([...STANDARD_LARGE_TEXT_FIELDS, ...customRefnames]);
27
+ return [...set];
50
28
  }
51
- // Structural HTML patterns that indicate the content is HTML (not markdown)
29
+ // ---------------------------------------------------------------------------
30
+ // HTML detection (pattern-based heuristic)
31
+ // ---------------------------------------------------------------------------
52
32
  const HTML_STRUCTURAL_PATTERNS = [
53
- /<div[^>]*>/i, // <div> tags
54
- /<p[^>]*>/i, // <p> tags
55
- /<h[1-6][^>]*>/i, // HTML heading tags
56
- /<strong[^>]*>/i, // <strong> tags (instead of **)
57
- /<em[^>]*>/i, // <em> tags (instead of *)
58
- /<ul[^>]*>/i, // HTML unordered lists
59
- /<ol[^>]*>/i, // HTML ordered lists
60
- /<li[^>]*>/i, // List items in HTML
61
- /<table[^>]*>/i, // HTML tables
62
- /<tr[^>]*>/i, // Table rows
63
- /<td[^>]*>/i, // Table cells
64
- /<span[^>]*>/i, // Span elements (ADO often uses these)
33
+ /<div[^>]*>/i,
34
+ /<p[^>]*>/i,
35
+ /<h[1-6][^>]*>/i,
36
+ /<strong[^>]*>/i,
37
+ /<em[^>]*>/i,
38
+ /<ul[^>]*>/i,
39
+ /<ol[^>]*>/i,
40
+ /<li[^>]*>/i,
41
+ /<table[^>]*>/i,
42
+ /<tr[^>]*>/i,
43
+ /<td[^>]*>/i,
44
+ /<span[^>]*>/i,
65
45
  ];
66
- // Markdown patterns that indicate the content is already markdown
67
46
  const MARKDOWN_PATTERNS = [
68
- /^#{1,6}\s+/m, // Markdown headings (# Heading)
69
- /\*\*[^*]+\*\*/, // Bold text with **
70
- /^\s*[-*+]\s+/m, // Unordered list items (-, *, +)
71
- /^\s*\d+\.\s+/m, // Numbered list items (1. item)
72
- /\[.+?\]\(.+?\)/, // Markdown links [text](url)
73
- /```[\s\S]*?```/, // Fenced code blocks
74
- /`[^`]+`/, // Inline code
75
- /^\s*>\s+/m, // Block quotes
47
+ /^#{1,6}\s+/m,
48
+ /\*\*[^*]+\*\*/,
49
+ /^\s*[-*+]\s+/m,
50
+ /^\s*\d+\.\s+/m,
51
+ /\[.+?\]\(.+?\)/,
52
+ /```[\s\S]*?```/,
53
+ /`[^`]+`/,
54
+ /^\s*>\s+/m,
76
55
  ];
77
- /**
78
- * Check if content is HTML format (as opposed to markdown)
79
- *
80
- * @param content - The content string to check
81
- * @returns true if content appears to be HTML, false if markdown or empty
82
- */
83
56
  export function isHtmlContent(content) {
84
- if (!content?.trim()) {
57
+ if (!content?.trim())
85
58
  return false;
86
- }
87
- // If clear markdown syntax is present, treat as markdown (even if some inline HTML exists)
88
- // This handles cases where users mix markdown with allowed inline HTML
89
59
  for (const pattern of MARKDOWN_PATTERNS) {
90
- if (pattern.test(content)) {
60
+ if (pattern.test(content))
91
61
  return false;
92
- }
93
62
  }
94
- // Check for structural HTML patterns that indicate this is HTML content
95
63
  for (const pattern of HTML_STRUCTURAL_PATTERNS) {
96
- if (pattern.test(content)) {
64
+ if (pattern.test(content))
97
65
  return true;
98
- }
99
66
  }
100
- // Default to markdown (plain text is valid markdown)
101
67
  return false;
102
68
  }
103
- /**
104
- * Check field formats for a work item
105
- *
106
- * Primary fields (Description, Acceptance Criteria): If HTML, work item is not ready for sync.
107
- * Secondary fields (How to Test, Deployment fields): If HTML, warn but allow sync (skip those fields).
108
- *
109
- * @param workItem - The work item object from ADO API
110
- * @returns Object with field format information
111
- */
112
- export function checkFieldFormats(workItem) {
69
+ export function checkFieldFormats(workItem, additionalRefnames = []) {
113
70
  const fields = workItem.fields || {};
114
- const fieldConfig = getAdditionalFieldConfig();
115
71
  const description = fields['System.Description'] || '';
116
72
  const acceptanceCriteria = fields['Microsoft.VSTS.Common.AcceptanceCriteria'] || '';
73
+ const reproSteps = fields['Microsoft.VSTS.TCM.ReproSteps'] || '';
117
74
  const descriptionIsHtml = isHtmlContent(description);
118
75
  const acceptanceCriteriaIsHtml = isHtmlContent(acceptanceCriteria);
119
- // Check additional fields
76
+ const reproStepsIsHtml = isHtmlContent(reproSteps);
120
77
  const warnings = [];
121
- const additionalFields = {
122
- howToTest: 'not_present',
123
- deploymentInformation: 'not_present',
124
- predeploymentSteps: 'not_present',
125
- postdeploymentSteps: 'not_present',
126
- };
127
- // Helper to check an additional field
128
- const checkAdditionalField = (fieldName, configKey) => {
129
- const content = fields[fieldName];
130
- if (!content || !content.trim()) {
131
- return 'not_present';
78
+ const additionalFields = {};
79
+ for (const refname of additionalRefnames) {
80
+ if (refname === 'System.Description' ||
81
+ refname === 'Microsoft.VSTS.Common.AcceptanceCriteria' ||
82
+ refname === 'Microsoft.VSTS.TCM.ReproSteps') {
83
+ // Covered by the dedicated slots above.
84
+ continue;
132
85
  }
133
- if (isHtmlContent(content)) {
134
- warnings.push(`${ADDITIONAL_FIELD_DISPLAY_NAMES[configKey]} is HTML - will be skipped`);
135
- return 'html';
86
+ const content = fields[refname];
87
+ if (!content || (typeof content === 'string' && !content.trim())) {
88
+ additionalFields[refname] = 'not_present';
89
+ continue;
136
90
  }
137
- return 'markdown';
138
- };
139
- additionalFields.howToTest = checkAdditionalField(fieldConfig.howToTest, 'howToTest');
140
- additionalFields.deploymentInformation = checkAdditionalField(fieldConfig.deploymentInformation, 'deploymentInformation');
141
- additionalFields.predeploymentSteps = checkAdditionalField(fieldConfig.predeploymentSteps, 'predeploymentSteps');
142
- additionalFields.postdeploymentSteps = checkAdditionalField(fieldConfig.postdeploymentSteps, 'postdeploymentSteps');
91
+ if (typeof content === 'string' && isHtmlContent(content)) {
92
+ warnings.push(`${refname} is HTML - will be skipped`);
93
+ additionalFields[refname] = 'html';
94
+ }
95
+ else {
96
+ additionalFields[refname] = 'markdown';
97
+ }
98
+ }
99
+ let reproStepsFormat = 'not_present';
100
+ if (reproSteps && reproSteps.trim()) {
101
+ reproStepsFormat = reproStepsIsHtml ? 'html' : 'markdown';
102
+ }
143
103
  return {
144
104
  description: descriptionIsHtml ? 'html' : 'markdown',
145
105
  acceptanceCriteria: acceptanceCriteriaIsHtml ? 'html' : 'markdown',
146
- ready: !descriptionIsHtml && !acceptanceCriteriaIsHtml,
106
+ reproSteps: reproStepsFormat,
107
+ ready: !descriptionIsHtml && !acceptanceCriteriaIsHtml && reproStepsFormat !== 'html',
147
108
  warnings,
148
109
  additionalFields,
149
110
  details: {
150
111
  descriptionLength: description.length,
151
112
  acceptanceCriteriaLength: acceptanceCriteria.length,
152
- }
113
+ reproStepsLength: reproSteps.length,
114
+ },
153
115
  };
154
116
  }
155
- /**
156
- * Get human-readable conversion instructions
157
- */
158
117
  export function getConversionInstructions() {
159
118
  return `To convert HTML fields to markdown in Azure DevOps:
160
119
  1. Open the work item in Azure DevOps
@@ -1 +1 @@
1
- {"version":3,"file":"html-detection.js","sourceRoot":"","sources":["../../src/sync/html-detection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"html-detection.js","sourceRoot":"","sources":["../../src/sync/html-detection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Image Reference Handler
3
+ *
4
+ * Detects and rewrites image references in ADO work item content.
5
+ *
6
+ * Two source forms are recognised:
7
+ * - HTML <img src="..."> (raw HTML, typically pre-conversion)
8
+ * - Markdown ![alt](src) (post Turndown conversion or hand-written)
9
+ *
10
+ * Each <img>/![] reference is parsed; if the URL points at an ADO work item
11
+ * attachment (`_apis/wit/attachments/{guid}`), we extract the GUID, fileName,
12
+ * and project GUID. Callers can then download the file and rewrite the source
13
+ * to a local relative path — or, on push, walk the manifest and rewrite local
14
+ * paths back to the original ADO URL.
15
+ */
16
+ export interface AdoAttachmentRef {
17
+ /** Attachment GUID extracted from the URL path */
18
+ guid: string;
19
+ /** Filename inferred from the `fileName` query param or the alt text */
20
+ fileName: string;
21
+ /** Project GUID from the URL (e.g. e4476c68-75d1-4d4b-...) */
22
+ projectGuid?: string;
23
+ /** The full original URL */
24
+ originalUrl: string;
25
+ }
26
+ export interface ImageRef {
27
+ /** Original src/url string from the markdown or HTML */
28
+ originalSrc: string;
29
+ /** True when the src is an HTML <img src="..."> tag (vs markdown ![]()) */
30
+ isHtmlTag: boolean;
31
+ /** Index of the match in the source string (for ordered processing) */
32
+ index: number;
33
+ /** Length of the matched substring */
34
+ length: number;
35
+ /** Parsed ADO attachment metadata, when the URL is an ADO attachment */
36
+ adoAttachment?: AdoAttachmentRef;
37
+ }
38
+ /**
39
+ * Parse an ADO work item attachment URL.
40
+ *
41
+ * Recognised patterns:
42
+ * https://dev.azure.com/{org}/{projectGuid}/_apis/wit/attachments/{guid}?fileName=image.png&...
43
+ * https://dev.azure.com/{org}/_apis/wit/attachments/{guid}?fileName=image.png&...
44
+ *
45
+ * Returns null when the URL is not an ADO attachment URL.
46
+ */
47
+ export declare function parseAdoAttachmentUrl(url: string): AdoAttachmentRef | null;
48
+ /**
49
+ * Extract every image reference from a string of HTML or markdown content.
50
+ *
51
+ * Returns refs in source order. Both HTML <img> tags and markdown ![]() are
52
+ * detected. Refs whose URL parses as an ADO attachment have `adoAttachment`
53
+ * populated.
54
+ */
55
+ export declare function extractImageRefs(content: string): ImageRef[];
56
+ /**
57
+ * Rewrite image src/url values in content using a mapper function.
58
+ *
59
+ * The mapper receives the original src and the parsed ADO attachment (if any)
60
+ * and returns the new src to substitute. Returning null/undefined leaves the
61
+ * original src in place.
62
+ *
63
+ * Both HTML <img src="..."> and markdown ![](src) forms are rewritten.
64
+ */
65
+ export declare function rewriteImageSrcs(content: string, mapper: (src: string, ado: AdoAttachmentRef | undefined) => string | null | undefined): string;
66
+ //# sourceMappingURL=image-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-handler.d.ts","sourceRoot":"","sources":["../../src/sync/image-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;IACnB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,aAAa,CAAC,EAAE,gBAAgB,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAgC1E;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmC5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,GAAG,SAAS,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,GACpF,MAAM,CA6BR"}