@mcp-consultant-tools/azure-devops 30.0.0-beta.15 → 30.0.0-beta.18
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/build/cli/commands/work-item-commands.d.ts.map +1 -1
- package/build/cli/commands/work-item-commands.js +34 -0
- package/build/cli/commands/work-item-commands.js.map +1 -1
- package/build/services/sync-service.d.ts.map +1 -1
- package/build/services/sync-service.js +67 -14
- package/build/services/sync-service.js.map +1 -1
- package/build/services/work-item-service.d.ts +25 -0
- package/build/services/work-item-service.d.ts.map +1 -1
- package/build/services/work-item-service.js +48 -0
- package/build/services/work-item-service.js.map +1 -1
- package/build/sync/annotation-parser.d.ts +49 -0
- package/build/sync/annotation-parser.d.ts.map +1 -0
- package/build/sync/annotation-parser.js +81 -0
- package/build/sync/annotation-parser.js.map +1 -0
- package/build/sync/field-aliases.d.ts +35 -0
- package/build/sync/field-aliases.d.ts.map +1 -0
- package/build/sync/field-aliases.js +76 -0
- package/build/sync/field-aliases.js.map +1 -0
- package/build/sync/html-converter.d.ts.map +1 -1
- package/build/sync/html-converter.js +12 -2
- package/build/sync/html-converter.js.map +1 -1
- package/build/sync/html-detection.d.ts +18 -65
- package/build/sync/html-detection.d.ts.map +1 -1
- package/build/sync/html-detection.js +72 -113
- package/build/sync/html-detection.js.map +1 -1
- package/build/sync/image-handler.d.ts +66 -0
- package/build/sync/image-handler.d.ts.map +1 -0
- package/build/sync/image-handler.js +135 -0
- package/build/sync/image-handler.js.map +1 -0
- package/build/sync/image-manifest.d.ts +66 -0
- package/build/sync/image-manifest.d.ts.map +1 -0
- package/build/sync/image-manifest.js +96 -0
- package/build/sync/image-manifest.js.map +1 -0
- package/build/sync/image-sync.d.ts +88 -0
- package/build/sync/image-sync.d.ts.map +1 -0
- package/build/sync/image-sync.js +274 -0
- package/build/sync/image-sync.js.map +1 -0
- package/build/sync/index.d.ts +7 -0
- package/build/sync/index.d.ts.map +1 -1
- package/build/sync/index.js +7 -0
- package/build/sync/index.js.map +1 -1
- package/build/sync/legacy-mappings.d.ts +37 -0
- package/build/sync/legacy-mappings.d.ts.map +1 -0
- package/build/sync/legacy-mappings.js +75 -0
- package/build/sync/legacy-mappings.js.map +1 -0
- package/build/sync/markdown-serializer.d.ts +54 -60
- package/build/sync/markdown-serializer.d.ts.map +1 -1
- package/build/sync/markdown-serializer.js +607 -554
- package/build/sync/markdown-serializer.js.map +1 -1
- package/build/sync/template-loader.d.ts +56 -0
- package/build/sync/template-loader.d.ts.map +1 -0
- package/build/sync/template-loader.js +138 -0
- package/build/sync/template-loader.js.map +1 -0
- package/build/sync/templates/bug.md +25 -0
- package/build/sync/templates/epic.md +23 -0
- package/build/sync/templates/feature.md +23 -0
- package/build/sync/templates/task.md +14 -0
- package/build/sync/templates/user-story.md +26 -0
- package/build/tool-examples.d.ts +4 -0
- package/build/tool-examples.d.ts.map +1 -1
- package/build/tool-examples.js +17 -0
- package/build/tool-examples.js.map +1 -1
- package/build/tools/work-item-tools.d.ts.map +1 -1
- package/build/tools/work-item-tools.js +31 -1
- package/build/tools/work-item-tools.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse annotated body sections from a synced markdown file.
|
|
3
|
+
*
|
|
4
|
+
* Sections are `##`-level headings. A section is "annotated" (i.e. syncs to
|
|
5
|
+
* an ADO field) when the first non-empty line after the heading is an HTML
|
|
6
|
+
* comment of the form `<!-- ado-field: REFNAME -->`. Unannotated sections
|
|
7
|
+
* are preserved verbatim but never pushed to ADO.
|
|
8
|
+
*/
|
|
9
|
+
const SECTION_HEADING_RE = /^##\s+(.+?)\s*$/;
|
|
10
|
+
const ANNOTATION_RE = /^<!--\s*ado-field:\s*([A-Za-z0-9_.\-]+)\s*-->$/;
|
|
11
|
+
/**
|
|
12
|
+
* Parse the body of a markdown file (everything after frontmatter) into
|
|
13
|
+
* annotated and local-only sections.
|
|
14
|
+
*/
|
|
15
|
+
export function parseAnnotations(body) {
|
|
16
|
+
const lines = body.split('\n');
|
|
17
|
+
const annotated = [];
|
|
18
|
+
const localOnly = [];
|
|
19
|
+
// Find all section boundaries (## headings).
|
|
20
|
+
const sections = [];
|
|
21
|
+
for (let i = 0; i < lines.length; i++) {
|
|
22
|
+
const match = lines[i].match(SECTION_HEADING_RE);
|
|
23
|
+
if (match) {
|
|
24
|
+
sections.push({ lineIndex: i, heading: match[1].trim() });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Everything before the first ## heading is preamble.
|
|
28
|
+
const preambleEnd = sections.length > 0 ? sections[0].lineIndex : lines.length;
|
|
29
|
+
const preamble = lines.slice(0, preambleEnd).join('\n').trim();
|
|
30
|
+
for (let idx = 0; idx < sections.length; idx++) {
|
|
31
|
+
const current = sections[idx];
|
|
32
|
+
const next = sections[idx + 1];
|
|
33
|
+
const bodyStart = current.lineIndex + 1;
|
|
34
|
+
const bodyEnd = next ? next.lineIndex : lines.length;
|
|
35
|
+
// Find first non-empty line in the body to check for annotation.
|
|
36
|
+
let firstNonEmpty = bodyStart;
|
|
37
|
+
while (firstNonEmpty < bodyEnd && lines[firstNonEmpty].trim() === '') {
|
|
38
|
+
firstNonEmpty++;
|
|
39
|
+
}
|
|
40
|
+
const annotationMatch = firstNonEmpty < bodyEnd ? lines[firstNonEmpty].trim().match(ANNOTATION_RE) : null;
|
|
41
|
+
if (annotationMatch) {
|
|
42
|
+
// Annotated section: content is everything AFTER the annotation line.
|
|
43
|
+
const content = lines.slice(firstNonEmpty + 1, bodyEnd).join('\n');
|
|
44
|
+
annotated.push({
|
|
45
|
+
refname: annotationMatch[1],
|
|
46
|
+
content: stripTrailingSeparators(content).trim(),
|
|
47
|
+
heading: current.heading,
|
|
48
|
+
lineIndex: current.lineIndex,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// Local-only section: everything between this heading and the next.
|
|
53
|
+
const content = lines.slice(bodyStart, bodyEnd).join('\n');
|
|
54
|
+
localOnly.push({
|
|
55
|
+
heading: current.heading,
|
|
56
|
+
content: stripTrailingSeparators(content).trim(),
|
|
57
|
+
lineIndex: current.lineIndex,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { annotated, localOnly, preamble };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build an annotated `##` section as a string.
|
|
65
|
+
*/
|
|
66
|
+
export function serializeAnnotatedSection(heading, refname, content) {
|
|
67
|
+
const body = content.trim() || '';
|
|
68
|
+
return `## ${heading}\n<!-- ado-field: ${refname} -->\n\n${body}\n`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Check whether a body has any `<!-- ado-field: ... -->` annotations.
|
|
72
|
+
* Used to decide between annotated parsing and legacy fallback.
|
|
73
|
+
*/
|
|
74
|
+
export function hasAnnotations(body) {
|
|
75
|
+
return /<!--\s*ado-field:\s*[A-Za-z0-9_.\-]+\s*-->/.test(body);
|
|
76
|
+
}
|
|
77
|
+
function stripTrailingSeparators(content) {
|
|
78
|
+
// Strip trailing `---` horizontal rules used as visual separators between sections.
|
|
79
|
+
return content.replace(/(\n\s*---\s*)+\s*$/, '');
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=annotation-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"annotation-parser.js","sourceRoot":"","sources":["../../src/sync/annotation-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -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;
|
|
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
|
-
|
|
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":"AAAA;;;;;GAKG;AAEH,OAAO,eAAe,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC;IACnC,YAAY,EAAE,KAAK,EAAS,uBAAuB;IACnD,gBAAgB,EAAE,GAAG,EAAO,kBAAkB;IAC9C,cAAc,EAAE,QAAQ,EAAI,0BAA0B;IACtD,WAAW,EAAE,GAAG,EAAY,qBAAqB;IACjD,eAAe,EAAE,IAAI,EAAO,oBAAoB;CACjD,CAAC,CAAC;AAEH,0DAA0D;AAC1D,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE;IAC1B,MAAM,EAAE,CAAC,MAAM,CAAC;IAChB,WAAW,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO;CAClC,CAAC,CAAC;AAEH
|
|
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
|
|
2
|
+
* HTML detection for ADO large-text fields.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
-
*
|
|
12
|
-
*
|
|
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
|
-
*
|
|
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
|
|
15
|
+
export declare const STANDARD_LARGE_TEXT_FIELDS: readonly ["System.Description", "Microsoft.VSTS.Common.AcceptanceCriteria", "Microsoft.VSTS.TCM.ReproSteps"];
|
|
39
16
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
|
2
|
+
* HTML detection for ADO large-text fields.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
39
|
-
*
|
|
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
|
|
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
|
-
//
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// HTML detection (pattern-based heuristic)
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
52
32
|
const HTML_STRUCTURAL_PATTERNS = [
|
|
53
|
-
/<div[^>]*>/i,
|
|
54
|
-
/<p[^>]*>/i,
|
|
55
|
-
/<h[1-6][^>]*>/i,
|
|
56
|
-
/<strong[^>]*>/i,
|
|
57
|
-
/<em[^>]*>/i,
|
|
58
|
-
/<ul[^>]*>/i,
|
|
59
|
-
/<ol[^>]*>/i,
|
|
60
|
-
/<li[^>]*>/i,
|
|
61
|
-
/<table[^>]*>/i,
|
|
62
|
-
/<tr[^>]*>/i,
|
|
63
|
-
/<td[^>]*>/i,
|
|
64
|
-
/<span[^>]*>/i,
|
|
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,
|
|
69
|
-
/\*\*[^*]+\*\*/,
|
|
70
|
-
/^\s*[-*+]\s+/m,
|
|
71
|
-
/^\s*\d+\.\s+/m,
|
|
72
|
-
/\[.+?\]\(.+?\)/,
|
|
73
|
-
/```[\s\S]*?```/,
|
|
74
|
-
/`[^`]+`/,
|
|
75
|
-
/^\s*>\s+/m,
|
|
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
|
-
|
|
76
|
+
const reproStepsIsHtml = isHtmlContent(reproSteps);
|
|
120
77
|
const warnings = [];
|
|
121
|
-
const additionalFields = {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
86
|
+
const content = fields[refname];
|
|
87
|
+
if (!content || (typeof content === 'string' && !content.trim())) {
|
|
88
|
+
additionalFields[refname] = 'not_present';
|
|
89
|
+
continue;
|
|
136
90
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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":"AAAA
|
|
1
|
+
{"version":3,"file":"html-detection.js","sourceRoot":"","sources":["../../src/sync/html-detection.ts"],"names":[],"mappings":"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"}
|