pmp-gywd 3.3.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.
Files changed (126) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +567 -0
  3. package/bin/install.js +348 -0
  4. package/commands/gywd/add-phase.md +207 -0
  5. package/commands/gywd/anticipate.md +271 -0
  6. package/commands/gywd/bootstrap.md +336 -0
  7. package/commands/gywd/challenge.md +344 -0
  8. package/commands/gywd/check-drift.md +144 -0
  9. package/commands/gywd/complete-milestone.md +106 -0
  10. package/commands/gywd/consider-issues.md +202 -0
  11. package/commands/gywd/context.md +93 -0
  12. package/commands/gywd/create-roadmap.md +115 -0
  13. package/commands/gywd/deps.md +169 -0
  14. package/commands/gywd/digest.md +138 -0
  15. package/commands/gywd/discuss-milestone.md +47 -0
  16. package/commands/gywd/discuss-phase.md +60 -0
  17. package/commands/gywd/execute-plan.md +161 -0
  18. package/commands/gywd/extract-decisions.md +325 -0
  19. package/commands/gywd/health.md +150 -0
  20. package/commands/gywd/help.md +556 -0
  21. package/commands/gywd/history.md +278 -0
  22. package/commands/gywd/impact.md +317 -0
  23. package/commands/gywd/init.md +95 -0
  24. package/commands/gywd/insert-phase.md +227 -0
  25. package/commands/gywd/list-phase-assumptions.md +50 -0
  26. package/commands/gywd/map-codebase.md +84 -0
  27. package/commands/gywd/memory.md +159 -0
  28. package/commands/gywd/new-milestone.md +59 -0
  29. package/commands/gywd/new-project.md +315 -0
  30. package/commands/gywd/pause-work.md +123 -0
  31. package/commands/gywd/plan-fix.md +205 -0
  32. package/commands/gywd/plan-phase.md +93 -0
  33. package/commands/gywd/preview-plan.md +139 -0
  34. package/commands/gywd/profile.md +363 -0
  35. package/commands/gywd/progress.md +317 -0
  36. package/commands/gywd/remove-phase.md +338 -0
  37. package/commands/gywd/research-phase.md +91 -0
  38. package/commands/gywd/resume-work.md +40 -0
  39. package/commands/gywd/rollback.md +179 -0
  40. package/commands/gywd/status.md +42 -0
  41. package/commands/gywd/sync-github.md +234 -0
  42. package/commands/gywd/verify-work.md +71 -0
  43. package/commands/gywd/why.md +251 -0
  44. package/docs/COMMANDS.md +722 -0
  45. package/docs/CONTRIBUTING.md +342 -0
  46. package/docs/EXAMPLES.md +535 -0
  47. package/docs/GETTING-STARTED.md +262 -0
  48. package/docs/README.md +55 -0
  49. package/docs/RELEASING.md +159 -0
  50. package/get-your-work-done/core/agent-patterns.md +331 -0
  51. package/get-your-work-done/core/architecture.md +334 -0
  52. package/get-your-work-done/core/context-model-schema.json +154 -0
  53. package/get-your-work-done/core/decisions-schema.json +193 -0
  54. package/get-your-work-done/core/learning-state-schema.json +133 -0
  55. package/get-your-work-done/core/profile-schema.json +257 -0
  56. package/get-your-work-done/references/adaptive-decomposition.md +175 -0
  57. package/get-your-work-done/references/checkpoints.md +287 -0
  58. package/get-your-work-done/references/confidence-scoring.md +169 -0
  59. package/get-your-work-done/references/continuation-format.md +255 -0
  60. package/get-your-work-done/references/git-integration.md +254 -0
  61. package/get-your-work-done/references/plan-format.md +428 -0
  62. package/get-your-work-done/references/principles.md +157 -0
  63. package/get-your-work-done/references/questioning.md +162 -0
  64. package/get-your-work-done/references/research-pitfalls.md +215 -0
  65. package/get-your-work-done/references/scope-estimation.md +172 -0
  66. package/get-your-work-done/references/tdd.md +263 -0
  67. package/get-your-work-done/templates/codebase/architecture.md +255 -0
  68. package/get-your-work-done/templates/codebase/concerns.md +310 -0
  69. package/get-your-work-done/templates/codebase/conventions.md +307 -0
  70. package/get-your-work-done/templates/codebase/integrations.md +280 -0
  71. package/get-your-work-done/templates/codebase/stack.md +186 -0
  72. package/get-your-work-done/templates/codebase/structure.md +285 -0
  73. package/get-your-work-done/templates/codebase/testing.md +480 -0
  74. package/get-your-work-done/templates/config.json +18 -0
  75. package/get-your-work-done/templates/context.md +161 -0
  76. package/get-your-work-done/templates/continue-here.md +78 -0
  77. package/get-your-work-done/templates/discovery.md +146 -0
  78. package/get-your-work-done/templates/issues.md +32 -0
  79. package/get-your-work-done/templates/milestone-archive.md +123 -0
  80. package/get-your-work-done/templates/milestone-context.md +93 -0
  81. package/get-your-work-done/templates/milestone.md +115 -0
  82. package/get-your-work-done/templates/phase-prompt.md +303 -0
  83. package/get-your-work-done/templates/project.md +184 -0
  84. package/get-your-work-done/templates/research.md +529 -0
  85. package/get-your-work-done/templates/roadmap.md +196 -0
  86. package/get-your-work-done/templates/state.md +210 -0
  87. package/get-your-work-done/templates/summary.md +273 -0
  88. package/get-your-work-done/templates/uat-issues.md +143 -0
  89. package/get-your-work-done/workflows/complete-milestone.md +643 -0
  90. package/get-your-work-done/workflows/create-milestone.md +416 -0
  91. package/get-your-work-done/workflows/create-roadmap.md +481 -0
  92. package/get-your-work-done/workflows/discovery-phase.md +293 -0
  93. package/get-your-work-done/workflows/discuss-milestone.md +236 -0
  94. package/get-your-work-done/workflows/discuss-phase.md +247 -0
  95. package/get-your-work-done/workflows/execute-phase.md +1625 -0
  96. package/get-your-work-done/workflows/list-phase-assumptions.md +178 -0
  97. package/get-your-work-done/workflows/map-codebase.md +434 -0
  98. package/get-your-work-done/workflows/plan-phase.md +488 -0
  99. package/get-your-work-done/workflows/research-phase.md +436 -0
  100. package/get-your-work-done/workflows/resume-project.md +287 -0
  101. package/get-your-work-done/workflows/transition.md +580 -0
  102. package/get-your-work-done/workflows/verify-work.md +202 -0
  103. package/lib/automation/dependency-analyzer.js +635 -0
  104. package/lib/automation/doc-generator.js +643 -0
  105. package/lib/automation/index.js +42 -0
  106. package/lib/automation/test-generator.js +628 -0
  107. package/lib/context/context-analyzer.js +554 -0
  108. package/lib/context/context-cache.js +426 -0
  109. package/lib/context/context-predictor.js +622 -0
  110. package/lib/context/index.js +44 -0
  111. package/lib/memory/confidence-calibrator.js +484 -0
  112. package/lib/memory/feedback-collector.js +551 -0
  113. package/lib/memory/global-memory.js +465 -0
  114. package/lib/memory/index.js +75 -0
  115. package/lib/memory/pattern-aggregator.js +487 -0
  116. package/lib/memory/team-sync.js +501 -0
  117. package/lib/profile/index.js +24 -0
  118. package/lib/profile/pattern-learner.js +303 -0
  119. package/lib/profile/profile-manager.js +445 -0
  120. package/lib/questioning/index.js +49 -0
  121. package/lib/questioning/question-engine.js +311 -0
  122. package/lib/questioning/question-templates.js +315 -0
  123. package/lib/validators/command-validator.js +188 -0
  124. package/lib/validators/index.js +29 -0
  125. package/lib/validators/schema-validator.js +183 -0
  126. package/package.json +61 -0
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Command Validator
3
+ *
4
+ * Validates GYWD command markdown files for structure and content.
5
+ * Zero external dependencies.
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ /**
12
+ * Parse YAML frontmatter from markdown content
13
+ * @param {string} content - Markdown content
14
+ * @returns {object|null} Parsed frontmatter or null if not found
15
+ */
16
+ function parseFrontmatter(content) {
17
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
18
+ if (!frontmatterMatch) {
19
+ return null;
20
+ }
21
+
22
+ const frontmatter = {};
23
+ const lines = frontmatterMatch[1].split('\n');
24
+
25
+ for (const line of lines) {
26
+ const colonIndex = line.indexOf(':');
27
+ if (colonIndex > 0) {
28
+ const key = line.slice(0, colonIndex).trim();
29
+ let value = line.slice(colonIndex + 1).trim();
30
+
31
+ // Handle arrays (lines starting with -)
32
+ if (value === '') {
33
+ const arrayLines = [];
34
+ const startIndex = lines.indexOf(line);
35
+ for (let i = startIndex + 1; i < lines.length; i++) {
36
+ if (lines[i].trim().startsWith('-')) {
37
+ arrayLines.push(lines[i].trim().slice(1).trim());
38
+ } else if (lines[i].trim() && !lines[i].startsWith(' ')) {
39
+ break;
40
+ }
41
+ }
42
+ if (arrayLines.length > 0) {
43
+ value = arrayLines;
44
+ }
45
+ }
46
+
47
+ frontmatter[key] = value;
48
+ }
49
+ }
50
+
51
+ return frontmatter;
52
+ }
53
+
54
+ /**
55
+ * Required sections for a valid command file
56
+ */
57
+ const REQUIRED_SECTIONS = ['objective'];
58
+ const PROCESS_SECTIONS = ['process', 'reference', 'output'];
59
+
60
+ /**
61
+ * Validate a command file's structure
62
+ * @param {string} content - File content
63
+ * @param {string} fileName - File name for error messages
64
+ * @returns {{valid: boolean, errors: string[], warnings: string[], frontmatter: object|null}}
65
+ */
66
+ function validateCommandStructure(content, _fileName) {
67
+ const errors = [];
68
+ const warnings = [];
69
+
70
+ // Check for frontmatter
71
+ const frontmatter = parseFrontmatter(content);
72
+ if (!frontmatter) {
73
+ warnings.push('Missing YAML frontmatter');
74
+ } else {
75
+ // Validate frontmatter fields
76
+ if (!frontmatter.name) {
77
+ errors.push('Frontmatter missing required field: name');
78
+ }
79
+ if (!frontmatter.description) {
80
+ errors.push('Frontmatter missing required field: description');
81
+ }
82
+ }
83
+
84
+ // Check for required sections
85
+ for (const section of REQUIRED_SECTIONS) {
86
+ const regex = new RegExp(`<${section}[^>]*>`, 'i');
87
+ if (!regex.test(content)) {
88
+ errors.push(`Missing required section: <${section}>`);
89
+ }
90
+ }
91
+
92
+ // Check for at least one process-type section
93
+ const hasProcessSection = PROCESS_SECTIONS.some(section => {
94
+ const regex = new RegExp(`<${section}[^>]*>`, 'i');
95
+ return regex.test(content);
96
+ });
97
+
98
+ if (!hasProcessSection) {
99
+ errors.push(`Missing <process>, <reference>, or <output> section`);
100
+ }
101
+
102
+ return {
103
+ valid: errors.length === 0,
104
+ errors,
105
+ warnings,
106
+ frontmatter,
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Extract workflow references from command content
112
+ * @param {string} content - File content
113
+ * @returns {string[]} Array of referenced workflow paths
114
+ */
115
+ function extractWorkflowReferences(content) {
116
+ const references = [];
117
+ const refMatches = content.matchAll(/@~\/.claude\/([^\s\n]+)/g);
118
+
119
+ for (const match of refMatches) {
120
+ // Strip trailing punctuation
121
+ const refPath = match[1].replace(/[`.,)]+$/, '');
122
+ if (refPath.includes('get-your-work-done/workflows/')) {
123
+ references.push(path.basename(refPath));
124
+ }
125
+ }
126
+
127
+ return references;
128
+ }
129
+
130
+ /**
131
+ * Validate workflow references exist
132
+ * @param {string[]} references - Array of workflow file names
133
+ * @param {string} workflowsDir - Path to workflows directory
134
+ * @returns {string[]} Array of missing workflow files
135
+ */
136
+ function validateWorkflowReferences(references, workflowsDir) {
137
+ const missing = [];
138
+
139
+ for (const ref of references) {
140
+ const workflowPath = path.join(workflowsDir, ref);
141
+ if (!fs.existsSync(workflowPath)) {
142
+ missing.push(ref);
143
+ }
144
+ }
145
+
146
+ return missing;
147
+ }
148
+
149
+ /**
150
+ * Validate a command file
151
+ * @param {string} filePath - Path to the command file
152
+ * @param {string} workflowsDir - Path to workflows directory
153
+ * @returns {{valid: boolean, errors: string[], warnings: string[]}}
154
+ */
155
+ function validateCommandFile(filePath, workflowsDir) {
156
+ if (!fs.existsSync(filePath)) {
157
+ return { valid: false, errors: [`File not found: ${filePath}`], warnings: [] };
158
+ }
159
+
160
+ const content = fs.readFileSync(filePath, 'utf8');
161
+ const fileName = path.basename(filePath);
162
+
163
+ // Validate structure
164
+ const structureResult = validateCommandStructure(content, fileName);
165
+
166
+ // Validate workflow references
167
+ const references = extractWorkflowReferences(content);
168
+ const missingWorkflows = validateWorkflowReferences(references, workflowsDir);
169
+
170
+ const errors = [...structureResult.errors];
171
+ for (const missing of missingWorkflows) {
172
+ errors.push(`Referenced workflow not found: ${missing}`);
173
+ }
174
+
175
+ return {
176
+ valid: errors.length === 0,
177
+ errors,
178
+ warnings: structureResult.warnings,
179
+ };
180
+ }
181
+
182
+ module.exports = {
183
+ parseFrontmatter,
184
+ validateCommandStructure,
185
+ extractWorkflowReferences,
186
+ validateWorkflowReferences,
187
+ validateCommandFile,
188
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * GYWD Validators
3
+ *
4
+ * Central export for all validation modules.
5
+ * Zero external dependencies.
6
+ */
7
+
8
+ const schemaValidator = require('./schema-validator');
9
+ const commandValidator = require('./command-validator');
10
+
11
+ module.exports = {
12
+ // Schema validation
13
+ validateJsonSyntax: schemaValidator.validateJsonSyntax,
14
+ validateSchemaStructure: schemaValidator.validateSchemaStructure,
15
+ validateDataAgainstSchema: schemaValidator.validateDataAgainstSchema,
16
+ loadSchema: schemaValidator.loadSchema,
17
+ validateFile: schemaValidator.validateFile,
18
+
19
+ // Command validation
20
+ parseFrontmatter: commandValidator.parseFrontmatter,
21
+ validateCommandStructure: commandValidator.validateCommandStructure,
22
+ extractWorkflowReferences: commandValidator.extractWorkflowReferences,
23
+ validateWorkflowReferences: commandValidator.validateWorkflowReferences,
24
+ validateCommandFile: commandValidator.validateCommandFile,
25
+
26
+ // Namespaced exports
27
+ schema: schemaValidator,
28
+ command: commandValidator,
29
+ };
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Schema Validator
3
+ *
4
+ * Validates JSON schemas and data files against schemas.
5
+ * Zero external dependencies - uses native JSON parsing.
6
+ */
7
+
8
+ const fs = require('fs');
9
+
10
+ /**
11
+ * Validate JSON syntax
12
+ * @param {string} content - JSON string to validate
13
+ * @returns {{valid: boolean, error?: string, data?: object}}
14
+ */
15
+ function validateJsonSyntax(content) {
16
+ try {
17
+ const data = JSON.parse(content);
18
+ return { valid: true, data };
19
+ } catch (error) {
20
+ return { valid: false, error: error.message };
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Validate schema structure (basic validation without external deps)
26
+ * @param {object} schema - The schema object to validate
27
+ * @returns {string[]} Array of issues found
28
+ */
29
+ function validateSchemaStructure(schema) {
30
+ const issues = [];
31
+
32
+ if (!schema.$schema) {
33
+ issues.push('Missing $schema declaration');
34
+ }
35
+
36
+ if (!schema.title) {
37
+ issues.push('Missing title');
38
+ }
39
+
40
+ if (!schema.type && !schema.properties && !schema.definitions) {
41
+ issues.push('Schema has no type, properties, or definitions');
42
+ }
43
+
44
+ // Check for valid type if present
45
+ const validTypes = ['object', 'array', 'string', 'number', 'boolean', 'null', 'integer'];
46
+ if (schema.type && !validTypes.includes(schema.type)) {
47
+ issues.push(`Invalid type: ${schema.type}`);
48
+ }
49
+
50
+ // Check required is an array if present
51
+ if (schema.required && !Array.isArray(schema.required)) {
52
+ issues.push('Required must be an array');
53
+ }
54
+
55
+ // Check properties is an object if present
56
+ if (schema.properties && typeof schema.properties !== 'object') {
57
+ issues.push('Properties must be an object');
58
+ }
59
+
60
+ return issues;
61
+ }
62
+
63
+ /**
64
+ * Validate data against a schema (basic validation)
65
+ * @param {object} data - The data to validate
66
+ * @param {object} schema - The schema to validate against
67
+ * @returns {{valid: boolean, errors: string[]}}
68
+ */
69
+ function validateDataAgainstSchema(data, schema) {
70
+ const errors = [];
71
+
72
+ // Type validation
73
+ if (schema.type) {
74
+ const actualType = Array.isArray(data) ? 'array' : typeof data;
75
+ if (schema.type === 'integer') {
76
+ if (typeof data !== 'number' || !Number.isInteger(data)) {
77
+ errors.push(`Expected integer, got ${typeof data}`);
78
+ }
79
+ } else if (actualType !== schema.type) {
80
+ errors.push(`Expected ${schema.type}, got ${actualType}`);
81
+ }
82
+ }
83
+
84
+ // Required properties validation
85
+ if (schema.required && schema.type === 'object') {
86
+ for (const prop of schema.required) {
87
+ if (!(prop in data)) {
88
+ errors.push(`Missing required property: ${prop}`);
89
+ }
90
+ }
91
+ }
92
+
93
+ // Enum validation
94
+ if (schema.enum && !schema.enum.includes(data)) {
95
+ errors.push(`Value must be one of: ${schema.enum.join(', ')}`);
96
+ }
97
+
98
+ // Nested properties validation
99
+ if (schema.properties && typeof data === 'object' && data !== null) {
100
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
101
+ if (key in data) {
102
+ const result = validateDataAgainstSchema(data[key], propSchema);
103
+ for (const err of result.errors) {
104
+ errors.push(`${key}: ${err}`);
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ // Array items validation
111
+ if (schema.items && Array.isArray(data)) {
112
+ data.forEach((item, index) => {
113
+ const result = validateDataAgainstSchema(item, schema.items);
114
+ for (const err of result.errors) {
115
+ errors.push(`[${index}]: ${err}`);
116
+ }
117
+ });
118
+ }
119
+
120
+ return { valid: errors.length === 0, errors };
121
+ }
122
+
123
+ /**
124
+ * Load and validate a schema file
125
+ * @param {string} schemaPath - Path to the schema file
126
+ * @returns {{valid: boolean, schema?: object, errors: string[]}}
127
+ */
128
+ function loadSchema(schemaPath) {
129
+ if (!fs.existsSync(schemaPath)) {
130
+ return { valid: false, errors: [`Schema file not found: ${schemaPath}`] };
131
+ }
132
+
133
+ const content = fs.readFileSync(schemaPath, 'utf8');
134
+ const parseResult = validateJsonSyntax(content);
135
+
136
+ if (!parseResult.valid) {
137
+ return { valid: false, errors: [`Invalid JSON: ${parseResult.error}`] };
138
+ }
139
+
140
+ const structureIssues = validateSchemaStructure(parseResult.data);
141
+ if (structureIssues.length > 0) {
142
+ return { valid: false, schema: parseResult.data, errors: structureIssues };
143
+ }
144
+
145
+ return { valid: true, schema: parseResult.data, errors: [] };
146
+ }
147
+
148
+ /**
149
+ * Validate a data file against a schema file
150
+ * @param {string} dataPath - Path to the data file
151
+ * @param {string} schemaPath - Path to the schema file
152
+ * @returns {{valid: boolean, errors: string[]}}
153
+ */
154
+ function validateFile(dataPath, schemaPath) {
155
+ // Load schema
156
+ const schemaResult = loadSchema(schemaPath);
157
+ if (!schemaResult.valid) {
158
+ return { valid: false, errors: schemaResult.errors.map(e => `Schema: ${e}`) };
159
+ }
160
+
161
+ // Load data
162
+ if (!fs.existsSync(dataPath)) {
163
+ return { valid: false, errors: [`Data file not found: ${dataPath}`] };
164
+ }
165
+
166
+ const content = fs.readFileSync(dataPath, 'utf8');
167
+ const parseResult = validateJsonSyntax(content);
168
+
169
+ if (!parseResult.valid) {
170
+ return { valid: false, errors: [`Invalid JSON: ${parseResult.error}`] };
171
+ }
172
+
173
+ // Validate data against schema
174
+ return validateDataAgainstSchema(parseResult.data, schemaResult.schema);
175
+ }
176
+
177
+ module.exports = {
178
+ validateJsonSyntax,
179
+ validateSchemaStructure,
180
+ validateDataAgainstSchema,
181
+ loadSchema,
182
+ validateFile,
183
+ };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "pmp-gywd",
3
+ "version": "3.3.0",
4
+ "description": "PMP (GYWD - Get Your Work Done) - Decision-aware, context-engineered development system for Claude Code. Understands WHY code exists, not just WHAT it does.",
5
+ "bin": {
6
+ "pmp-gywd": "bin/install.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "commands",
11
+ "docs",
12
+ "get-your-work-done",
13
+ "lib"
14
+ ],
15
+ "scripts": {
16
+ "test": "jest",
17
+ "test:watch": "jest --watch",
18
+ "test:coverage": "jest --coverage",
19
+ "test:ci": "jest --ci --coverage --reporters=default --reporters=jest-junit",
20
+ "lint": "eslint bin lib scripts tests --ext .js",
21
+ "lint:fix": "eslint bin lib scripts tests --ext .js --fix",
22
+ "validate:schemas": "node scripts/validate-schemas.js",
23
+ "validate:commands": "node scripts/validate-commands.js",
24
+ "validate:all": "npm run validate:schemas && npm run validate:commands",
25
+ "generate:tests": "node scripts/generate-tests.js",
26
+ "generate:docs": "node scripts/generate-docs.js",
27
+ "analyze:deps": "node scripts/analyze-dependencies.js",
28
+ "precommit": "npm run lint && npm run validate:all && npm test",
29
+ "prepublishOnly": "npm run test:ci && npm run validate:all",
30
+ "release": "npm run lint && npm run validate:all && npm test"
31
+ },
32
+ "keywords": [
33
+ "claude",
34
+ "claude-code",
35
+ "ai",
36
+ "meta-prompting",
37
+ "context-engineering",
38
+ "spec-driven-development",
39
+ "decision-intelligence",
40
+ "pmp",
41
+ "gywd"
42
+ ],
43
+ "author": "cyberbloke9",
44
+ "license": "MIT",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/cyberbloke9/pmp-gywd.git"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/cyberbloke9/pmp-gywd/issues"
51
+ },
52
+ "homepage": "https://github.com/cyberbloke9/pmp-gywd#readme",
53
+ "engines": {
54
+ "node": ">=16.7.0"
55
+ },
56
+ "devDependencies": {
57
+ "eslint": "^8.57.0",
58
+ "jest": "^29.7.0",
59
+ "jest-junit": "^16.0.0"
60
+ }
61
+ }