@dsh-gis/dsh-opengis-skill 0.1.0 → 0.1.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 (37) hide show
  1. package/.editorconfig +19 -0
  2. package/CHANGELOG.md +96 -0
  3. package/LICENSE +21 -21
  4. package/README.md +91 -30
  5. package/THIRD_PARTY_NOTICES.md +37 -10
  6. package/assets/gdal/reference/raster-tools.md +538 -538
  7. package/assets/gdal/reference/vector-tools.md +149 -149
  8. package/assets/gdal-api/reference/raster-api.md +424 -424
  9. package/assets/gdal-api/reference/vector-api.md +724 -724
  10. package/assets/geometry-api-net/reference/geometry-types.md +243 -243
  11. package/assets/geopandas/reference/core-api.md +221 -221
  12. package/assets/geotools/reference/raster-rendering.md +108 -108
  13. package/assets/geotools/reference/vector-io.md +149 -149
  14. package/assets/jts/reference/geometry-operations.md +303 -303
  15. package/assets/mapsui/SKILL.md +283 -283
  16. package/assets/opengis-all/reference/data-stages.md +608 -608
  17. package/assets/opengis-all/reference/workflow-cheatsheets.md +324 -324
  18. package/assets/opengis-utils-for-java/reference/common-tasks.md +384 -384
  19. package/assets/opengis-utils-for-net/reference/code-patterns.md +124 -124
  20. package/assets/opengis-utils-for-net/reference/util-classes.md +291 -291
  21. package/assets/pyqgis/reference/processing-rendering.md +292 -292
  22. package/assets/pyqgis/reference/vector-raster-layers.md +318 -318
  23. package/assets/qgis-process/SKILL.md +414 -414
  24. package/assets/qgis-process/reference/algorithm-cheatsheet.md +194 -194
  25. package/assets/shapely/reference/operations-reference.md +199 -199
  26. package/cordis.patch.yml +6 -3
  27. package/lib/catalog.d.ts +50 -0
  28. package/lib/catalog.js +238 -0
  29. package/lib/index.d.ts +47 -6
  30. package/lib/index.js +32 -44
  31. package/lib/markdown-links.d.ts +11 -0
  32. package/lib/markdown-links.js +103 -0
  33. package/lib/skill-file.js +104 -17
  34. package/locale/en.json +1 -1
  35. package/locale/zh.json +1 -1
  36. package/package.json +11 -5
  37. package/upstream.json +64 -2
package/lib/skill-file.js CHANGED
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Minimal SKILL.md frontmatter reader shared by the plugin, the sync script,
3
- * and the validation script. The upstream opengis-skills files keep `name` and
4
- * `description` on single lines, so only scalar fields are read; list fields
5
- * such as `tags` are ignored.
3
+ * and the validation script.
4
+ *
5
+ * Vendored opengis-skills files keep "name" and "description" on single lines,
6
+ * so only scalar fields are read; list fields such as "tags" are ignored.
7
+ * Multi-line block scalars are rejected instead of being silently truncated,
8
+ * and double-quoted scalars support the common YAML escape sequences.
6
9
  *
7
10
  * @module @dsh-gis/dsh-opengis-skill/skill-file
8
11
  */
@@ -13,9 +16,39 @@ const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u;
13
16
  /** The public kebab-case skill-name grammar used by the skill registry. */
14
17
  const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
15
18
 
16
- /** One `key: value` scalar line inside the frontmatter block. */
19
+ /** One "key: value" scalar line inside the frontmatter block. */
17
20
  const FIELD = /^([A-Za-z][A-Za-z0-9_-]*):[ \t]*(.*)$/u;
18
21
 
22
+ /** A complete single-line double-quoted YAML scalar. */
23
+ const DOUBLE_QUOTED = /^"((?:[^"\\]|\\.)*)"$/u;
24
+
25
+ /** A complete single-line single-quoted YAML scalar. */
26
+ const SINGLE_QUOTED = /^'((?:[^']|'')*)'$/u;
27
+
28
+ /** One double-quoted escape sequence: \uXXXX, \UXXXXXXXX, \xXX, or \c. */
29
+ const DOUBLE_QUOTED_ESCAPE = /\\(u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8}|x[0-9A-Fa-f]{2}|[\s\S])/gu;
30
+
31
+ /** The one-character YAML escape sequences this reader understands. */
32
+ const SIMPLE_ESCAPES = new Map([
33
+ ["0", "\0"],
34
+ ["a", "\x07"],
35
+ ["b", "\b"],
36
+ ["t", "\t"],
37
+ ["n", "\n"],
38
+ ["v", "\v"],
39
+ ["f", "\f"],
40
+ ["r", "\r"],
41
+ ["e", "\x1b"],
42
+ [" ", " "],
43
+ ['"', '"'],
44
+ ["/", "/"],
45
+ ["\\", "\\"],
46
+ ["N", "\u0085"],
47
+ ["_", "\u00a0"],
48
+ ["L", "\u2028"],
49
+ ["P", "\u2029"]
50
+ ]);
51
+
19
52
  /**
20
53
  * Parse one SKILL.md file into the registry fields and the instruction body.
21
54
  *
@@ -25,19 +58,31 @@ const FIELD = /^([A-Za-z][A-Za-z0-9_-]*):[ \t]*(.*)$/u;
25
58
  * @throws when the frontmatter, the name, or the description is missing or malformed.
26
59
  */
27
60
  export function parseSkillFile(raw, path) {
28
- const match = FRONTMATTER.exec(raw);
29
- if (match === null) throw new Error(`${path}: missing YAML frontmatter`);
61
+ if (typeof raw !== "string") throw new TypeError(path + ": SKILL.md content must be a string");
62
+ const text = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;
63
+ const match = FRONTMATTER.exec(text);
64
+ if (match === null) throw new Error(path + ": missing YAML frontmatter");
30
65
  const fields = new Map();
31
66
  for (const line of match[1].split(/\r?\n/u)) {
32
67
  const entry = FIELD.exec(line);
33
68
  if (entry === null) continue;
34
- fields.set(entry[1], unquote(entry[2].trim()));
69
+ fields.set(entry[1], parseScalar(entry[2].trim(), path + ': "' + entry[1] + '"'));
35
70
  }
36
71
  const name = fields.get("name");
37
72
  const description = fields.get("description");
38
- if (!isSkillName(name)) throw new Error(`${path}: "name" must be kebab-case, got ${JSON.stringify(name)}`);
39
- if (typeof description !== "string" || description.length === 0) throw new Error(`${path}: "description" is required`);
40
- return { name, description, content: raw.slice(match[0].length).trim() };
73
+ if (!isSkillName(name)) throw new Error(path + ': "name" must be kebab-case, got ' + JSON.stringify(name));
74
+ if (typeof description !== "string" || description.length === 0) throw new Error(path + ': "description" is required');
75
+ return { name, description, content: normalizeBody(text.slice(match[0].length)) };
76
+ }
77
+
78
+ /**
79
+ * Normalize the instruction body: trim the edges and use LF line endings.
80
+ *
81
+ * @param body - the raw text after the closing frontmatter marker.
82
+ * @returns the instruction body handed to the skill registry.
83
+ */
84
+ function normalizeBody(body) {
85
+ return body.trim().replace(/\r\n?/gu, "\n");
41
86
  }
42
87
 
43
88
  /**
@@ -50,13 +95,55 @@ export function isSkillName(value) {
50
95
  return typeof value === "string" && SKILL_NAME.test(value);
51
96
  }
52
97
 
53
- /** Remove one layer of YAML single or double quotes from a scalar value. */
54
- function unquote(value) {
55
- if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
56
- return value.slice(1, -1).replace(/\\(["\\])/gu, "$1");
98
+ /**
99
+ * Parse one frontmatter scalar value.
100
+ *
101
+ * Only single-line scalars are supported. A leading quote must close on the
102
+ * same line, and a block scalar indicator ("|" or ">") is rejected so that an
103
+ * upstream format change fails loudly instead of silently truncating a
104
+ * description.
105
+ *
106
+ * @param value - the trimmed text after "key:".
107
+ * @param subject - the error-message prefix, for example "file: \"description\"".
108
+ * @returns the decoded scalar text.
109
+ */
110
+ function parseScalar(value, subject) {
111
+ if (value.length === 0) return "";
112
+ if (value.startsWith('"')) {
113
+ const quoted = DOUBLE_QUOTED.exec(value);
114
+ if (quoted === null) throw new Error(subject + ": malformed or unterminated double-quoted scalar");
115
+ return unescapeDoubleQuoted(quoted[1], subject);
116
+ }
117
+ if (value.startsWith("'")) {
118
+ const quoted = SINGLE_QUOTED.exec(value);
119
+ if (quoted === null) throw new Error(subject + ": malformed or unterminated single-quoted scalar");
120
+ return quoted[1].replaceAll("''", "'");
57
121
  }
58
- if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
59
- return value.slice(1, -1).replace(/''/gu, "'");
122
+ if (value.startsWith(">") || value.startsWith("|")) {
123
+ throw new Error(subject + ": multi-line block scalars are not supported");
60
124
  }
61
- return value;
125
+ const comment = value.search(/[ \t]#/u);
126
+ return (comment === -1 ? value : value.slice(0, comment)).trim();
127
+ }
128
+
129
+ /**
130
+ * Decode the body of a double-quoted YAML scalar.
131
+ *
132
+ * @param body - the text between the surrounding quotes.
133
+ * @param subject - the error-message prefix.
134
+ * @returns the decoded text.
135
+ */
136
+ function unescapeDoubleQuoted(body, subject) {
137
+ return body.replace(DOUBLE_QUOTED_ESCAPE, (match, escape) => {
138
+ if (escape.length > 1) {
139
+ const codePoint = Number.parseInt(escape.slice(1), 16);
140
+ if (!Number.isInteger(codePoint) || codePoint > 0x10ffff) {
141
+ throw new Error(subject + ': invalid unicode escape "\\' + escape + '"');
142
+ }
143
+ return String.fromCodePoint(codePoint);
144
+ }
145
+ const simple = SIMPLE_ESCAPES.get(escape);
146
+ if (simple === undefined) throw new Error(subject + ': unsupported escape sequence "\\' + escape + '"');
147
+ return simple;
148
+ });
62
149
  }
package/locale/en.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "OpenGIS Skills",
4
- "description": "32 GIS skills from opengis-skills — GDAL, QGIS, GeoServer, PostGIS, JTS, CesiumJS, OpenLayers, Leaflet, deck.gl and more — loaded on demand."
4
+ "description": "GIS skills from opengis-skills — GDAL, QGIS, GeoServer, PostGIS, JTS, CesiumJS, OpenLayers, Leaflet, deck.gl and more — loaded on demand."
5
5
  }
6
6
  }
package/locale/zh.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "OpenGIS 技能包",
4
- "description": "来自 opengis-skills 的 32 个 GIS 技能:GDAL、QGIS、GeoServer、PostGIS、JTS、CesiumJS、OpenLayers、Leaflet、deck.gl 等,按需加载。"
4
+ "description": "来自 opengis-skills 的 GIS 技能:GDAL、QGIS、GeoServer、PostGIS、JTS、CesiumJS、OpenLayers、Leaflet、deck.gl 等,按需加载。"
5
5
  }
6
6
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dsh-gis/dsh-opengis-skill",
3
- "version": "0.1.0",
4
- "description": "OpenGIS skill pack for DeepSeek Harness: 32 GIS skills from opengis-skills (GDAL, QGIS, GeoServer, PostGIS, JTS, CesiumJS, OpenLayers and more), loaded on demand.",
3
+ "version": "0.1.2",
4
+ "description": "OpenGIS skill pack for DeepSeek Harness: the gis/ skills from opengis-skills (GDAL, QGIS, GeoServer, PostGIS, JTS, CesiumJS, OpenLayers and more), loaded on demand.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -28,10 +28,12 @@
28
28
  "icon.svg",
29
29
  "upstream.json",
30
30
  "README.md",
31
- "THIRD_PARTY_NOTICES.md"
31
+ "THIRD_PARTY_NOTICES.md",
32
+ "CHANGELOG.md",
33
+ ".editorconfig"
32
34
  ],
33
35
  "engines": {
34
- "dsh": ">=0.2.1-rc.2 <0.3.0-0",
36
+ "dsh": ">=0.2.0-rc.2 <0.3.0-0",
35
37
  "node": ">=22"
36
38
  },
37
39
  "dsh": {
@@ -44,7 +46,8 @@
44
46
  "sync:check": "node scripts/sync-opengis-skills.mjs --check",
45
47
  "validate": "node scripts/validate-skills.mjs",
46
48
  "test": "node --test",
47
- "prepublishOnly": "npm run validate && npm test"
49
+ "check": "npm run validate && npm test",
50
+ "prepublishOnly": "npm run check"
48
51
  },
49
52
  "repository": {
50
53
  "type": "git",
@@ -60,7 +63,10 @@
60
63
  "dsh-plugin",
61
64
  "cordis",
62
65
  "skills",
66
+ "skill",
63
67
  "skill-pack",
68
+ "dsh-bundle",
69
+ "gis-skills",
64
70
  "gis",
65
71
  "geospatial",
66
72
  "opengis",
package/upstream.json CHANGED
@@ -1,7 +1,9 @@
1
1
  {
2
+ "schemaVersion": 1,
2
3
  "repository": "https://github.com/znlgis/opengis-skills",
3
4
  "commit": "c18c097b93a1e01792bdcc2d7c0f5c7f0f664988",
4
- "syncedAt": "2026-10-19T03:29:13.262Z",
5
+ "syncedAt": "2026-10-19T04:05:40.659Z",
6
+ "hashAlgorithm": "sha256",
5
7
  "skillCount": 32,
6
8
  "skills": [
7
9
  "cesiumjs",
@@ -36,5 +38,65 @@
36
38
  "qgis-server",
37
39
  "shapely",
38
40
  "sharpmap"
39
- ]
41
+ ],
42
+ "files": {
43
+ "cesiumjs/SKILL.md": "d15c900b0cb4eb3d9cbba9d64f3bb2abcf49c42bdf4cfdc07074c2536691fda0",
44
+ "deck-gl/SKILL.md": "fd41bdddf7bef5bae98c8ee06cf5fbb1d940edf7ca38c3187d17c05bb22027d6",
45
+ "gdal-api/SKILL.md": "6d6c573a0c815eb56a5ba37a825fabb24616babb3e1b957031bf52a8042b7cfe",
46
+ "gdal-api/reference/raster-api.md": "c0d4526602bf3e2e3f80abeaa972997c132e3f6fbbabd79347d035a3cd1f1c84",
47
+ "gdal-api/reference/vector-api.md": "5907be299d5e969f294c25a53737454f0e5abcd6d74e0d3831ddd46d9f18d03f",
48
+ "gdal/SKILL.md": "3db004f046ab3882a7136ecb093826b9eddb74f53e8a098e9cfcffdb889b772f",
49
+ "gdal/reference/data-formats.md": "443731a0f13330ab90e7ecc3e8e46d85519fbcd7013861dfa63f693668fdffd3",
50
+ "gdal/reference/raster-tools.md": "a3ed2dbb9571ce59973729e8cea5d68cf039d30bf39c98b614fb523a61541f97",
51
+ "gdal/reference/vector-tools.md": "284b5685f3f72b13865a8da2bef33637eb92820aa0b3cfec57f8c745a1fe3966",
52
+ "geometry-api-java/SKILL.md": "7b6d41dc8c4289b6c7744daba4dbb7b61afc80709bea42ec7def5b5855bc28a0",
53
+ "geometry-api-net/SKILL.md": "d53ff7d848524ce132b5100f7443561f285502addac5b42f8f03ae68ec2449cb",
54
+ "geometry-api-net/reference/geometry-types.md": "121e6ef0f5c7fb053b8e14bf42e952629bfb8b0717626fd7744db59ca6cfb4e7",
55
+ "geopandas/SKILL.md": "26f00dea35b0a9a35ce9b0580b38ff4684ffeb64156b96437aec4e5d9e14db42",
56
+ "geopandas/reference/core-api.md": "1e233adb18510d7d4788cfc18ac47982b53784b09ce37d2171494e9beba4e05f",
57
+ "geopipe-agent/SKILL.md": "50c788dcb87259b1a1102d7c4aee7fc36c44bfef3b73029c84a650d693d100c3",
58
+ "geopipe-agent/reference/backends.md": "00d7658470e67661d6774eb827f9ab0ccbb2262604b3cb65a0382bf27d2b331f",
59
+ "geopipe-agent/reference/knowledge.md": "63b1d44d6f83d4e33011c35855d08b2c83cd07f2ec21722a120b1e2164685e07",
60
+ "geopipe-agent/reference/pipeline-schema.md": "9b6a7a87002fa2fa53f61441a0f9ace804b970ba9ea6b00b0308e2dba9a77cb6",
61
+ "geopipe-agent/reference/self-extension.md": "552be0bfaef58f3cb7f10567362c3c7328bea520b8ef61bda5f79f84529e16a1",
62
+ "geopipe-agent/reference/steps-reference.md": "3c1adaf5f9960ea9c99919cc61bb065e59ce2b77a6a029aa2a2e9977dc794bf8",
63
+ "geopipe-agent/reference/workflows.md": "e4e0fb32dd83b7f7b27de5a42f6a2d7eea71c4fae7e8c7764d7defe7340e1313",
64
+ "geoserver-cloud/SKILL.md": "f7943b645440f03bab9055201a7552b7e86be47c8ce27b5f1c57487107e4300a",
65
+ "geoserver-rest-api/SKILL.md": "64827e5b164687a2b7e44f668a30f52f6ea66bd205a953e03b7357a3db7427d2",
66
+ "geoserver-rest-api/reference/endpoints.md": "20fbb2ea7ccddcbb34f5ecb227d53127073a8109edbff9ad1bbcae07a3cf1be7",
67
+ "geoserver/SKILL.md": "0e266a7975d80c7facda8dac8d1bce3e519fca11afa2bef631b10f8023769158",
68
+ "geotools/SKILL.md": "1bf76ab5870adba3ca4faca7887b2081e64bd04cdb6f5154ad08f14002337d3a",
69
+ "geotools/reference/raster-rendering.md": "3b1876ad39d8c11756b1ff5eac8cda9dab0d94f958f8cafa4f32d9ff5fa6ebd3",
70
+ "geotools/reference/vector-io.md": "2dca5de4e149bc9e79bd5bfcb345c36e45da06a9d86a53bc28869168f55902f1",
71
+ "grass-gis/SKILL.md": "fc908f4bf14a026cf41997415f4a7d6d43b94914d1062e31f86c133dbc7343bc",
72
+ "jts/SKILL.md": "a8b739bc67e65f11cc714939e7e13e98736246693f61e07a662b1470b468888e",
73
+ "jts/reference/geometry-operations.md": "a242f1f0185136baa60f632072c041fb734185b844da25c37ef1f4e3b2685124",
74
+ "leaflet/SKILL.md": "44238568e0682acb72bfd68d97f25fb75608e9e2683965abfb272ba0d5bd46da",
75
+ "mapbox-gl-js/SKILL.md": "6c10bf3f7c3968f41f3bc030c1ca0c49f51fcea2b4c42a4f347cf845f9845759",
76
+ "mapserver/SKILL.md": "df291706233d90be652405726740667f2b6d82eee6594dcadd7979daa4e15aaf",
77
+ "mapsui/SKILL.md": "1a9eac49a40477360348e9dc37ed3cde9143b6f7e7feeef6838c12c0578f213e",
78
+ "nettopologysuite/SKILL.md": "a045193f0e4a16a9876fc36909b256eeb93901c14c77db3c914a8f8e24c514f0",
79
+ "opengis-all/SKILL.md": "e4b31d462be1c52fed6b019bb45321be4a40723a0b0bb28f4e6f3631e0175ef4",
80
+ "opengis-all/reference/data-stages.md": "88d0182d8a96469b0ea8857a463517b3b38c1fde72b5599cad3958da7c8cb4ed",
81
+ "opengis-all/reference/workflow-cheatsheets.md": "3bb58ea3b966ab1bd85380cda1253056ff03accb97f37d863f3f96e7ce6dbdb7",
82
+ "opengis-daf/SKILL.md": "3d8c496ecb0c54cc837f83355a67ba0866787ba58ef151c077b6c40fe014c040",
83
+ "opengis-utils-for-java/SKILL.md": "bb883b9175ffafd649eda066cffe642a0bebe6d29cf6d03b389d71b1a97259a6",
84
+ "opengis-utils-for-java/reference/common-tasks.md": "a32f2e1181d9c49e1c8cc16bba1345c3d8aca6d42be1cc2afe2a330bd382b47a",
85
+ "opengis-utils-for-net/SKILL.md": "d0e63e540ec731904551ad5ffdaa8374430ea85e7439d88126102b32b78be560",
86
+ "opengis-utils-for-net/reference/code-patterns.md": "379a6a22e10b291639fa6ee07ad105522523420666ea52a5e588f19fc5c8a064",
87
+ "opengis-utils-for-net/reference/util-classes.md": "111fc1347edb8af6660b0f9614b5ecff4b6654ea19fe1549f6924eb8b3467db2",
88
+ "openlayers/SKILL.md": "6cfe6f10cc69e9e36b8db8e1a2896553144d6346061bdc433fbcf9dbc3ebe96e",
89
+ "pdal/SKILL.md": "eb4733ad80540b9911ca0a7aa6600bb0647f32dd088693c6f8855a0a45bff90d",
90
+ "postgis/SKILL.md": "2f83487427263b54217d49c08ffc9ce362867c6344e3e789e3246e6cc673c621",
91
+ "proj/SKILL.md": "42d1cb70c732f27fb1ec687737759278a70fa73e6bca97a0b2cff641ee16c0d9",
92
+ "pyqgis/SKILL.md": "1d24d2072aeaa519374dca7cca0bd75de91e187f882ac460f6340eaee4cd6a79",
93
+ "pyqgis/reference/processing-rendering.md": "138644c36ca3f30e746016bb7457fd3a2d18349c5740f02d1735ba6c59173a3e",
94
+ "pyqgis/reference/vector-raster-layers.md": "fec965918eff2d24c6e8f8c68379c52bb10727892f8d599e6c425760d168cd2d",
95
+ "qgis-process/SKILL.md": "d9613cf47ae041c079b24d453074e6c4f3b5477617c16e9024aca3356f751aff",
96
+ "qgis-process/reference/algorithm-cheatsheet.md": "d88720d80a2d78efefcf1ff9027b451f291bef5ef166fa9befcae484bfa91e8f",
97
+ "qgis-server/SKILL.md": "2cd0aa5b4a6b4372f96c88318cd36c035c26783a8b6f5a77fb5dc0752367affa",
98
+ "shapely/SKILL.md": "8cc848b6ca28d2dd50ff951ca44ae90cd0255a33f5e329a62f31828947006fb0",
99
+ "shapely/reference/operations-reference.md": "a984e9cd84dbfa3bc299284725639520883a85289f67a61d7b296a00665c2714",
100
+ "sharpmap/SKILL.md": "4bf964bcbd663009982e4cbe06ea80b9c8ee626bfcf7b98b615612f9356a7f72"
101
+ }
40
102
  }