xdrs-core 0.14.3 → 0.14.5
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/.xdrs/_core/adrs/principles/003-skill-standards.md +2 -0
- package/.xdrs/_core/adrs/principles/004-article-standards.md +1 -1
- package/.xdrs/_core/adrs/principles/006-research-standards.md +1 -0
- package/.xdrs/_core/adrs/principles/007-plan-standards.md +4 -3
- package/.xdrs/_core/adrs/principles/skills/001-lint/001-lint.test.int.report +3 -3
- package/.xdrs/_core/adrs/principles/skills/002-write-xdr/002-write-xdr.test.int.report +3 -3
- package/.xdrs/_core/adrs/principles/skills/003-write-skill/003-write-skill.test.int.report +3 -3
- package/.xdrs/_core/adrs/principles/skills/003-write-skill/SKILL.md +1 -1
- package/.xdrs/_core/adrs/principles/skills/004-write-article/004-write-article.test.int.report +8 -5
- package/.xdrs/_core/adrs/principles/skills/004-write-article/SKILL.md +1 -1
- package/.xdrs/_core/adrs/principles/skills/005-write-research/SKILL.md +1 -1
- package/.xdrs/_core/adrs/principles/skills/006-write-plan/SKILL.md +4 -4
- package/lib/testPrompt.js +20 -7
- package/lib/testPrompt.test.js +13 -0
- package/package.json +3 -2
|
@@ -39,6 +39,8 @@ Place a skill under the XDR type that matches the nature of the activity the ski
|
|
|
39
39
|
- **ADR skills** - architectural evaluation, pattern compliance checks, technology selection guidance (e.g. how to review an architecture diagram, how to assess API design)
|
|
40
40
|
- **BDR skills** - business process execution, market analysis, operations procedures, business rules
|
|
41
41
|
|
|
42
|
+
The `[subject]` component in the folder path MUST be one of the allowed subjects for the chosen type. The required list of allowed subjects per type is defined in `_core-adr-001`.
|
|
43
|
+
|
|
42
44
|
Quick test:
|
|
43
45
|
- "Is the skill about *how to implement or operate* something?" → EDR.
|
|
44
46
|
- "Is the skill about *how to evaluate or decide on* architecture?" → ADR.
|
|
@@ -21,7 +21,7 @@ Articles are Markdown documents placed inside a subject folder alongside decisio
|
|
|
21
21
|
- In more complex cases, an article may be an index of links to other articles, grouping related documentation into a single entry point that guides readers across a set of related topics.
|
|
22
22
|
- When an article tells readers which decisions to follow, it SHOULD check `Valid:` first to determine the convergence date, `Applied to:` second to determine context fit, and the decision text itself last. All documents present in the collection are considered active; articles must not present out-of-window or out-of-scope XDRs as current rules for the discussed context.
|
|
23
23
|
- Articles must remain consistent with the XDRs, Research documents, and Skills they reference. When a referenced artifact changes, the article must be reviewed and updated.
|
|
24
|
-
- Place an article in the subject folder that best matches its topic
|
|
24
|
+
- Place an article in the subject folder that best matches its topic using the required list of subjects per type defined in `_core-adr-001`. If an article spans more than one subject, place it in `principles`.
|
|
25
25
|
- For simple structure, flow, layout, or relationship indications, articles SHOULD prefer plain Markdown, tables, or ASCII art instead of external assets.
|
|
26
26
|
- Images and other local resource files referenced by an article SHOULD be used only when they are materially necessary and SHOULD live in `articles/assets/` next to the article files.
|
|
27
27
|
- Always use lowercase file names.
|
|
@@ -18,6 +18,7 @@ Research documents are Markdown files placed inside a subject folder alongside d
|
|
|
18
18
|
- `Research` is the artifact name. `researches/` is only the folder name used alongside `skills/` and `articles/`.
|
|
19
19
|
- Research documents MUST live under `researches/` inside the relevant subject folder:
|
|
20
20
|
`.xdrs/[scope]/[type]/[subject]/researches/[number]-[short-title].md`
|
|
21
|
+
- The `[subject]` component MUST be one of the allowed subjects for the chosen type. The required list of allowed subjects per type is defined in `_core-adr-001`.
|
|
21
22
|
- Research documents SHOULD stay focused on one investigated problem, comparison, or hypothesis.
|
|
22
23
|
- Research documents MUST state clearly what problem or question is being investigated, the relevant system or domain context, and why the subject matters in practice.
|
|
23
24
|
- Research documents MUST follow this section order: `Abstract`, `Introduction`, `Methods`, `Results`, `Discussion`, `Conclusion`, `References`.
|
|
@@ -22,7 +22,8 @@ Plans are Markdown documents placed inside a subject folder alongside decision r
|
|
|
22
22
|
- A plan can be high level, describing only one milestone, or more complex, describing a WBS (work breakdown structure) along with owners, multiple milestones in a tactical sequence, and checklists to verify completeness. Actual tasks performed by actors should normally be tracked in specialized software such as GitHub or Azure DevOps.
|
|
23
23
|
- The total time to deliver a plan should not be more than 2 years. If more time is needed, create a new plan later with what was learned.
|
|
24
24
|
- Plans MUST live under `plans/` inside the relevant subject folder: `.xdrs/[scope]/[type]/[subject]/plans/[number]-[short-title].md`
|
|
25
|
-
-
|
|
25
|
+
- The `[subject]` component MUST be one of the allowed subjects for the chosen type. The required list of allowed subjects per type is defined in `_core-adr-001`.
|
|
26
|
+
- Plans MUST include an `Expected end date:` field in ISO format (YYYY-MM-DD) inside the `## Proposed Solution` section.
|
|
26
27
|
- Always use lowercase file names.
|
|
27
28
|
- Never use emojis in plan content.
|
|
28
29
|
- Images and other local resource files referenced by a plan SHOULD live in `plans/assets/` next to the plan files.
|
|
@@ -70,12 +71,12 @@ E.g.: Our checkout abandon rate is 50%, and it's increasing over time.]
|
|
|
70
71
|
[Required. What we expect to achieve to solve the problem described above. Under 200 words.
|
|
71
72
|
E.g.: Reduce payment time in our App by 30% and fix the 3 most impactful bugs.]
|
|
72
73
|
|
|
74
|
+
Expected end date: YYYY-MM-DD
|
|
75
|
+
|
|
73
76
|
## Acceptance Criteria
|
|
74
77
|
|
|
75
78
|
[Optional. Used to make it clear what the expected result is and to create a way to verify when the goal is achieved. May include a short checklist. Under 100 words.]
|
|
76
79
|
|
|
77
|
-
Expected end date: YYYY-MM-DD
|
|
78
|
-
|
|
79
80
|
## Approach
|
|
80
81
|
|
|
81
82
|
[Optional. High level description about how to achieve the result and the strategy used, including how to engage people, projects, organize the work, how to learn unknowns, deal with risks, and distribute workload. May include a WBS with the hierarchy of the work. Under 300 words.]
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
"Review xdr 001-xdrs-core-ac1c8339": {
|
|
3
3
|
"result": "success",
|
|
4
4
|
"contextFiles": [
|
|
5
|
+
".github/skills/001-lint/SKILL.md",
|
|
5
6
|
".xdrs/_core/adrs/index.md",
|
|
6
7
|
".xdrs/_core/adrs/principles/001-xdrs-core.md",
|
|
7
8
|
".xdrs/_core/adrs/principles/002-xdr-standards.md",
|
|
8
|
-
".xdrs/
|
|
9
|
-
".xdrs/_local/bdrs/index.md",
|
|
9
|
+
".xdrs/_local/adrs/index.md",
|
|
10
10
|
".xdrs/index.md",
|
|
11
11
|
"AGENTS.md"
|
|
12
12
|
],
|
|
13
|
-
"contextHash": "
|
|
13
|
+
"contextHash": "080ea010bbb924d74c9196e7c3e0f6af"
|
|
14
14
|
},
|
|
15
15
|
"Reply ONLY with \"READY\" after checking i-a61b0904": {
|
|
16
16
|
"result": "success",
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"Create a very small XDR deciding to use -8b1a2ddd": {
|
|
3
3
|
"result": "success",
|
|
4
4
|
"contextFiles": [
|
|
5
|
+
".xdrs/_core/adrs/index.md",
|
|
5
6
|
".xdrs/_core/adrs/principles/001-xdrs-core.md",
|
|
6
7
|
".xdrs/_core/adrs/principles/002-xdr-standards.md",
|
|
7
|
-
".xdrs/
|
|
8
|
-
".xdrs/_local/bdrs/operations/001-agent-behavior-validation-procedure.md",
|
|
8
|
+
".xdrs/_core/adrs/principles/skills/002-write-xdr/SKILL.md",
|
|
9
9
|
".xdrs/index.md",
|
|
10
10
|
"AGENTS.md"
|
|
11
11
|
],
|
|
12
|
-
"contextHash": "
|
|
12
|
+
"contextHash": "8d030307248237f1775ab38126d354db"
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"Create a skill with instructions on how -a7079882": {
|
|
3
3
|
"result": "success",
|
|
4
4
|
"contextFiles": [
|
|
5
|
+
".xdrs/_core/adrs/index.md",
|
|
5
6
|
".xdrs/_core/adrs/principles/001-xdrs-core.md",
|
|
6
7
|
".xdrs/_core/adrs/principles/003-skill-standards.md",
|
|
7
|
-
".xdrs/
|
|
8
|
-
".xdrs/_local/bdrs/operations/001-agent-behavior-validation-procedure.md",
|
|
8
|
+
".xdrs/_core/adrs/principles/skills/003-write-skill/SKILL.md",
|
|
9
9
|
".xdrs/index.md",
|
|
10
10
|
"AGENTS.md"
|
|
11
11
|
],
|
|
12
|
-
"contextHash": "
|
|
12
|
+
"contextHash": "0bbe5ae297e8b1978c7020a7bb8779c1"
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -37,7 +37,7 @@ Quick test:
|
|
|
37
37
|
|
|
38
38
|
**Scope** — use `_local` unless the user explicitly names another scope.
|
|
39
39
|
|
|
40
|
-
**Subject** — pick the most specific match for the chosen type (
|
|
40
|
+
**Subject** — pick the most specific match for the chosen type (required list per type is in `_core-adr-001`):
|
|
41
41
|
- ADR subjects: `principles`, `application`, `data`, `integration`, `platform`, `controls`, `operations`
|
|
42
42
|
- BDR subjects: `principles`, `marketing`, `product`, `controls`, `operations`, `organization`, `finance`, `sustainability`
|
|
43
43
|
- EDR subjects: `principles`, `application`, `infra`, `observability`, `devops`, `governance`
|
package/.xdrs/_core/adrs/principles/skills/004-write-article/004-write-article.test.int.report
CHANGED
|
@@ -4,14 +4,17 @@
|
|
|
4
4
|
"contextFiles": [
|
|
5
5
|
".xdrs/_core/adrs/index.md",
|
|
6
6
|
".xdrs/_core/adrs/principles/001-xdrs-core.md",
|
|
7
|
+
".xdrs/_core/adrs/principles/002-xdr-standards.md",
|
|
8
|
+
".xdrs/_core/adrs/principles/003-skill-standards.md",
|
|
7
9
|
".xdrs/_core/adrs/principles/004-article-standards.md",
|
|
10
|
+
".xdrs/_core/adrs/principles/006-research-standards.md",
|
|
11
|
+
".xdrs/_core/adrs/principles/007-plan-standards.md",
|
|
8
12
|
".xdrs/_core/adrs/principles/articles/001-xdrs-overview.md",
|
|
9
|
-
".xdrs/_local/
|
|
10
|
-
".xdrs/_local/
|
|
13
|
+
".xdrs/_local/adrs/index.md",
|
|
14
|
+
".xdrs/_local/adrs/principles/researches/001-research-and-decision-lifecycle.md",
|
|
11
15
|
".xdrs/index.md",
|
|
12
|
-
"AGENTS.md"
|
|
13
|
-
"README.md"
|
|
16
|
+
"AGENTS.md"
|
|
14
17
|
],
|
|
15
|
-
"contextHash": "
|
|
18
|
+
"contextHash": "4935b72a3613a4fad7cc72e477afed7a"
|
|
16
19
|
}
|
|
17
20
|
}
|
|
@@ -48,7 +48,7 @@ If the topic spans multiple types, use `adrs`. Use the same rules as `002-write-
|
|
|
48
48
|
- **ADR**: system context, integration pattern, overarching architectural choice
|
|
49
49
|
- **EDR**: specific tool/library, coding practice, testing strategy, project structure, pipelines
|
|
50
50
|
|
|
51
|
-
**Subject** — pick the subject that best matches the article's topic (
|
|
51
|
+
**Subject** — pick the subject that best matches the article's topic (required list per type is in `_core-adr-001`).
|
|
52
52
|
If the article spans more than one subject, place it in `principles`.
|
|
53
53
|
|
|
54
54
|
### Phase 3: Assign a Number and Name
|
|
@@ -42,7 +42,7 @@ Consult `001-xdrs-core` while making each choice in this phase. The summaries be
|
|
|
42
42
|
- **ADR**: system context, integration pattern, overarching architectural choice
|
|
43
43
|
- **EDR**: specific tool/library, coding practice, testing strategy, project structure, pipelines
|
|
44
44
|
|
|
45
|
-
**Subject** — pick the most specific subject that matches the problem domain.
|
|
45
|
+
**Subject** — pick the most specific subject that matches the problem domain (required list per type is in `_core-adr-001`).
|
|
46
46
|
|
|
47
47
|
**Research number** — scan `.xdrs/[scope]/[type]/[subject]/researches/` for the highest existing number and increment by 1. Never reuse numbers from deleted research documents.
|
|
48
48
|
|
|
@@ -39,7 +39,7 @@ Consult `001-xdrs-core` while making each choice in this phase. The summaries be
|
|
|
39
39
|
- **ADR**: system context, integration pattern, overarching architectural choice
|
|
40
40
|
- **EDR**: specific tool/library, coding practice, testing strategy, project structure, pipelines
|
|
41
41
|
|
|
42
|
-
**Subject** — pick the subject that best matches the plan's topic. If the plan spans more than one subject, place it in `principles`.
|
|
42
|
+
**Subject** — pick the subject that best matches the plan's topic (required list per type is in `_core-adr-001`). If the plan spans more than one subject, place it in `principles`.
|
|
43
43
|
|
|
44
44
|
### Phase 3: Assign a Number and Name
|
|
45
45
|
|
|
@@ -75,12 +75,12 @@ Use the mandatory template from `007-plan-standards`:
|
|
|
75
75
|
|
|
76
76
|
[Required. What we expect to achieve. Under 200 words.]
|
|
77
77
|
|
|
78
|
+
Expected end date: YYYY-MM-DD
|
|
79
|
+
|
|
78
80
|
## Acceptance Criteria
|
|
79
81
|
|
|
80
82
|
[Optional. Expected result and how to verify the goal is achieved. Under 100 words.]
|
|
81
83
|
|
|
82
|
-
Expected end date: YYYY-MM-DD
|
|
83
|
-
|
|
84
84
|
## Approach
|
|
85
85
|
|
|
86
86
|
[Optional. Strategy and high-level how. Under 300 words.]
|
|
@@ -110,7 +110,7 @@ Rules to apply while drafting:
|
|
|
110
110
|
|
|
111
111
|
- Focus on the problem, solution, and approach. Avoid bloating with generic project management content.
|
|
112
112
|
- Link to Decisions the plan implements, Research that informs it, and Skills that guide execution.
|
|
113
|
-
- The Expected end date must be in ISO format (YYYY-MM-DD) and should not be more than 2 years from the plan creation date.
|
|
113
|
+
- The Expected end date must be in ISO format (YYYY-MM-DD), placed inside the `## Proposed Solution` section, and should not be more than 2 years from the plan creation date.
|
|
114
114
|
- If the plan scope is too large for 2 years, break it into multiple plans.
|
|
115
115
|
- Remember that this plan must be deleted after full implementation. Write it with that ephemeral nature in mind.
|
|
116
116
|
- Prefer plain Markdown, tables, or ASCII art for structure and flow.
|
package/lib/testPrompt.js
CHANGED
|
@@ -28,7 +28,7 @@ async function runPromptTest(config, inputPrompt, judgePrompt, verbose) {
|
|
|
28
28
|
try {
|
|
29
29
|
if (options.workspaceMode === 'copy') {
|
|
30
30
|
tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'xdrs-core-test-'));
|
|
31
|
-
effectiveWorkspace = copyWorkspace(originalWorkspace, path.join(tempRoot, 'workspace'), options.workspaceFilter, verbose);
|
|
31
|
+
effectiveWorkspace = copyWorkspace(originalWorkspace, path.join(tempRoot, 'workspace'), options.workspaceFilter, options.workspaceExclude, verbose);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
if(verbose) {
|
|
@@ -113,7 +113,9 @@ function copilotCmd(workspaceRoot = findGitRoot(process.cwd())) {
|
|
|
113
113
|
'-p',
|
|
114
114
|
'{PROMPT}'
|
|
115
115
|
],
|
|
116
|
-
promptCmdContinueFlag: '--continue'
|
|
116
|
+
promptCmdContinueFlag: '--continue',
|
|
117
|
+
workspaceFilter: ['AGENTS.md', '.xdrs/index.md', '.xdrs/_core/**'],
|
|
118
|
+
workspaceExclude: ['**/*.test.js', '**/*.test.int.js', '**/*.test.int.report']
|
|
117
119
|
};
|
|
118
120
|
}
|
|
119
121
|
|
|
@@ -153,6 +155,7 @@ function normalizeConfig(config) {
|
|
|
153
155
|
workspaceRoot: config.workspaceRoot ? path.resolve(config.workspaceRoot) : null,
|
|
154
156
|
workspaceMode,
|
|
155
157
|
workspaceFilter: normalizeWorkspaceFilter(config.workspaceFilter),
|
|
158
|
+
workspaceExclude: normalizeWorkspaceFilter(config.workspaceExclude),
|
|
156
159
|
env: normalizeEnv(config.env),
|
|
157
160
|
taskTimeoutMs: readTimeout(config.taskTimeoutMs, 'taskTimeoutMs'),
|
|
158
161
|
judgeTimeoutMs: readTimeout(config.judgeTimeoutMs, 'judgeTimeoutMs'),
|
|
@@ -667,7 +670,11 @@ async function runPrompt(config, inputPrompt, judgePrompt, id, verbose) {
|
|
|
667
670
|
function computeContextHash(model, inputPrompt, judgePrompt, contextFiles, workspaceRoot) {
|
|
668
671
|
const parts = [model, inputPrompt, judgePrompt];
|
|
669
672
|
for (const filePath of contextFiles) {
|
|
670
|
-
|
|
673
|
+
try {
|
|
674
|
+
parts.push(fs.readFileSync(path.join(workspaceRoot, filePath), 'utf8'));
|
|
675
|
+
} catch {
|
|
676
|
+
console.warn(`computeContextHash: file not found, ignoring: ${path.join(workspaceRoot, filePath)}`);
|
|
677
|
+
}
|
|
671
678
|
}
|
|
672
679
|
return crypto.createHash('md5').update(parts.join('\n')).digest('hex');
|
|
673
680
|
}
|
|
@@ -725,7 +732,7 @@ function findGitRoot(startPath) {
|
|
|
725
732
|
}
|
|
726
733
|
}
|
|
727
734
|
|
|
728
|
-
function copyWorkspace(sourcePath, targetPath, workspaceFilter, verbose) {
|
|
735
|
+
function copyWorkspace(sourcePath, targetPath, workspaceFilter, workspaceExclude, verbose) {
|
|
729
736
|
if(verbose) {
|
|
730
737
|
console.log(`Copying workspace from ${sourcePath} to ${targetPath}`);
|
|
731
738
|
}
|
|
@@ -736,12 +743,13 @@ function copyWorkspace(sourcePath, targetPath, workspaceFilter, verbose) {
|
|
|
736
743
|
rootPath: sourcePath,
|
|
737
744
|
ignoreContexts: [],
|
|
738
745
|
activeRealDirectories: new Set(),
|
|
739
|
-
workspaceFilter
|
|
746
|
+
workspaceFilter,
|
|
747
|
+
workspaceExclude
|
|
740
748
|
});
|
|
741
749
|
return targetPath;
|
|
742
750
|
}
|
|
743
751
|
|
|
744
|
-
function copyWorkspaceDirectory({ sourceDir, targetDir, rootPath, ignoreContexts, activeRealDirectories, workspaceFilter }) {
|
|
752
|
+
function copyWorkspaceDirectory({ sourceDir, targetDir, rootPath, ignoreContexts, activeRealDirectories, workspaceFilter, workspaceExclude }) {
|
|
745
753
|
const realSourceDir = fs.realpathSync(sourceDir);
|
|
746
754
|
if (activeRealDirectories.has(realSourceDir)) {
|
|
747
755
|
return;
|
|
@@ -782,7 +790,8 @@ function copyWorkspaceDirectory({ sourceDir, targetDir, rootPath, ignoreContexts
|
|
|
782
790
|
rootPath,
|
|
783
791
|
ignoreContexts: nextIgnoreContexts,
|
|
784
792
|
activeRealDirectories,
|
|
785
|
-
workspaceFilter
|
|
793
|
+
workspaceFilter,
|
|
794
|
+
workspaceExclude
|
|
786
795
|
});
|
|
787
796
|
continue;
|
|
788
797
|
}
|
|
@@ -791,6 +800,10 @@ function copyWorkspaceDirectory({ sourceDir, targetDir, rootPath, ignoreContexts
|
|
|
791
800
|
continue;
|
|
792
801
|
}
|
|
793
802
|
|
|
803
|
+
if (workspaceExclude && workspaceExclude.some((pattern) => minimatch(entryRelativePath, pattern, { dot: true }))) {
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
|
|
794
807
|
fs.copyFileSync(sourceEntryPath, targetEntryPath);
|
|
795
808
|
fs.chmodSync(targetEntryPath, (entryStats || fs.statSync(sourceEntryPath)).mode);
|
|
796
809
|
}
|
package/lib/testPrompt.test.js
CHANGED
|
@@ -89,6 +89,19 @@ test('workspaceFilter copies only files matching the glob pattern to temp worksp
|
|
|
89
89
|
expect(err).toBe('');
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
+
test('workspaceExclude removes files matching glob patterns from copied workspace', async () => {
|
|
93
|
+
const workspaceRoot = createWorkspace('exclude-pass');
|
|
94
|
+
fs.writeFileSync(path.join(workspaceRoot, 'notes.md'), 'notes content\n', 'utf8');
|
|
95
|
+
|
|
96
|
+
const err = await testPrompt(
|
|
97
|
+
createConfig(workspaceRoot, { workspaceExclude: ['*.md'] }),
|
|
98
|
+
'workspace-exclude-check: list files in the workspace',
|
|
99
|
+
'workspace-exclude-check: notes.md should not exist in the copied workspace, seed.txt should exist'
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
expect(err).toBe('');
|
|
103
|
+
});
|
|
104
|
+
|
|
92
105
|
test('copilotCmd defaults to the git repository root', () => {
|
|
93
106
|
const result = copilotCmd();
|
|
94
107
|
const command = result.promptCmd;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xdrs-core",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.5",
|
|
4
4
|
"description": "A standard way to organize Decision Records (XDRs) across scopes, subjects, and teams so that AI agents can reliably query and follow them.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
],
|
|
45
45
|
"exclude": [
|
|
46
46
|
"**/*.test.js",
|
|
47
|
-
"**/*.test.int.js"
|
|
47
|
+
"**/*.test.int.js",
|
|
48
|
+
"**/*.test.int.report"
|
|
48
49
|
]
|
|
49
50
|
},
|
|
50
51
|
"output": {
|