@mmerterden/multi-agent-pipeline 14.2.2 → 15.1.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.
- package/CHANGELOG.md +186 -6
- package/README.md +19 -12
- package/README.tr.md +19 -12
- package/SECURITY.md +43 -0
- package/docs/FIGMA_PIPELINE.md +3 -3
- package/docs/adr/0006-skills-core-external-split.md +1 -1
- package/docs/adr/0007-multi-tool-adapter-framework.md +1 -1
- package/docs/adr/0009-claude-stack-skills-plugin-only.md +31 -0
- package/docs/adr/README.md +1 -0
- package/docs/architecture.md +13 -13
- package/docs/ecosystem.md +31 -31
- package/docs/features.md +5 -5
- package/index.js +6 -1
- package/install/_codex-agents.mjs +11 -2
- package/install/_common.mjs +109 -3
- package/install/_dev-only-files.mjs +0 -1
- package/install/_platform-filter.mjs +54 -113
- package/install/_plugin-skills.mjs +36 -36
- package/install/claude.mjs +251 -61
- package/install/codex.mjs +28 -6
- package/install/copilot.mjs +69 -9
- package/install/index.mjs +9 -3
- package/install/templates/codex-instructions.md +1 -1
- package/install/templates/copilot-instructions.md +3 -3
- package/package.json +2 -3
- package/pipeline/commands/multi-agent/SKILL.md +2 -0
- package/pipeline/commands/multi-agent/analysis/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/build-optimize/SKILL.md +9 -9
- package/pipeline/commands/multi-agent/channels/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +186 -0
- package/pipeline/commands/multi-agent/dev/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-local/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +19 -4
- package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/prune-prompts/SKILL.md +81 -0
- package/pipeline/commands/multi-agent/refactor/SKILL.md +36 -1
- package/pipeline/commands/multi-agent/resume/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/{ship → resume-local}/SKILL.md +8 -8
- package/pipeline/commands/multi-agent/scan/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/setup/SKILL.md +5 -5
- package/pipeline/commands/multi-agent/stack/SKILL.md +62 -40
- package/pipeline/commands/multi-agent/store-ready/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/sync/SKILL.md +18 -11
- package/pipeline/commands/multi-agent/testflight-validation/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/uninstall/SKILL.md +2 -0
- package/pipeline/commands/multi-agent/update/SKILL.md +4 -4
- package/pipeline/lib/issue-fetcher.sh +1 -1
- package/pipeline/lib/parse-complaints.sh +316 -0
- package/pipeline/multi-agent-refs/channels/wiki.md +3 -3
- package/pipeline/multi-agent-refs/complaint-analysis-template.md +99 -0
- package/pipeline/multi-agent-refs/component-dispatch.md +6 -6
- package/pipeline/multi-agent-refs/cross-cli-contract.md +16 -16
- package/pipeline/multi-agent-refs/features/external-context-injection.md +1 -1
- package/pipeline/multi-agent-refs/features/stack-skill-routing.md +5 -5
- package/pipeline/multi-agent-refs/generate-issue.md +1 -1
- package/pipeline/multi-agent-refs/phases/modes.md +1 -1
- package/pipeline/multi-agent-refs/phases/operations.md +7 -1
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +7 -7
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +5 -5
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +3 -3
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +12 -12
- package/pipeline/multi-agent-refs/phases/phase-5-test.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-7-report.md +6 -0
- package/pipeline/multi-agent-refs/tracker-contract.md +3 -2
- package/pipeline/multi-agent-refs/wiki-capture.md +2 -2
- package/pipeline/preferences-template.json +18 -5
- package/pipeline/rules/figma-pipeline.md +2 -2
- package/pipeline/schemas/agent-state.schema.json +1 -1
- package/pipeline/schemas/complaint-analysis-spec.schema.json +216 -0
- package/pipeline/schemas/migrations/prefs-2.5.0-to-2.6.0.mjs +46 -0
- package/pipeline/schemas/prefs.schema.json +296 -66
- package/pipeline/schemas/token-budget.json +2 -2
- package/pipeline/scripts/README.md +4 -3
- package/pipeline/scripts/_stack-routing.mjs +79 -0
- package/pipeline/scripts/audit-log-rotate.sh +4 -1
- package/pipeline/scripts/build-skills-index.mjs +11 -0
- package/pipeline/scripts/build-stack-plugins.mjs +28 -60
- package/pipeline/scripts/check-derived-drift.mjs +55 -28
- package/pipeline/scripts/gc-worktrees.sh +4 -1
- package/pipeline/scripts/gen-skills-index.mjs +1 -1
- package/pipeline/scripts/match-skills.mjs +12 -2
- package/pipeline/scripts/migrate-prefs.mjs +33 -21
- package/pipeline/scripts/phase-tracker.sh +32 -5
- package/pipeline/scripts/phase0-exit-gate.mjs +3 -2
- package/pipeline/scripts/run-aggregator.mjs +7 -2
- package/pipeline/scripts/scan-agent-config.sh +1 -1
- package/pipeline/scripts/skill-conformance.mjs +165 -30
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +1 -1
- package/pipeline/scripts/test-gap-rules/android.json +25 -0
- package/pipeline/scripts/test-gap-rules/ios.json +34 -0
- package/pipeline/scripts/test-gap-rules/node.json +29 -0
- package/pipeline/scripts/test-gap-rules/python.json +25 -0
- package/pipeline/scripts/uninstall.mjs +160 -11
- package/pipeline/scripts/usage-report.mjs +426 -0
- package/pipeline/scripts/validate-complaint-doc.mjs +250 -0
- package/pipeline/scripts/validate-reviewer.mjs +9 -3
- package/pipeline/skills/.skill-manifest.json +156 -108
- package/pipeline/skills/.skills-index.json +449 -12
- package/pipeline/skills/shared/README.md +14 -10
- package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-complaint-analysis/SKILL.md +49 -0
- package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-prune-prompts/SKILL.md +83 -0
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +153 -90
- package/pipeline/skills/shared/core/{multi-agent-ship → multi-agent-resume-local}/SKILL.md +6 -6
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +89 -22
- package/pipeline/skills/shared/core/multi-agent-store-ready/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +8 -8
- package/pipeline/skills/shared/core/multi-agent-testflight-validation/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +1 -1
- package/pipeline/skills/shared/external/ios-coding-standard/modules/_TEMPLATE.yml +2 -2
- package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +368 -33
- package/pipeline/skills/shared/external/ios-coding-standard/references/swiftlint.draft.yml +1 -2
- package/pipeline/skills/shared/external/ios-coding-standard/scripts/check_structure.py +765 -0
- package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +75 -0
- package/pipeline/skills/shared/external/ios-module-structure/modules/_TEMPLATE.yml +131 -0
- package/pipeline/skills/shared/external/ios-module-structure/references/rules.yml +559 -0
- package/pipeline/skills/shared/external/ios-module-structure/scripts/check_structure.py +765 -0
- package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +53 -10
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +4 -3
- package/pipeline/skills/skills-index.md +7 -4
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/{owner}/multi-agent-pipeline/pipeline/schemas/complaint-analysis-spec.schema.json",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"title": "Multi-Agent Pipeline - /multi-agent:complaint-analysis run state",
|
|
6
|
+
"description": "Contract for state.complaintSpec, the run state of the customer-complaint triage command. Complaints arrive redacted (parse-complaints.sh strips PII before anything lands here), each one carries its Graylog evidence status, repo evidence rows, and a triage verdict: client / bff (our responsibility, root-cause analysis) or core (routing recommendation only) or insufficient-evidence.",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"required": ["runName", "language", "repos", "complaints"],
|
|
10
|
+
"properties": {
|
|
11
|
+
"runName": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"minLength": 1,
|
|
14
|
+
"description": "Run name supplied by the user or defaulted to complaints-<YYYYMMDD>."
|
|
15
|
+
},
|
|
16
|
+
"phase": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"enum": [
|
|
19
|
+
"intake",
|
|
20
|
+
"fetching_graylog",
|
|
21
|
+
"correlating_repos",
|
|
22
|
+
"triaging",
|
|
23
|
+
"drafting",
|
|
24
|
+
"awaiting_output_decision",
|
|
25
|
+
"dispatching",
|
|
26
|
+
"reporting",
|
|
27
|
+
"done"
|
|
28
|
+
],
|
|
29
|
+
"description": "Resume marker. Set after each phase boundary."
|
|
30
|
+
},
|
|
31
|
+
"language": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"enum": ["tr", "en"],
|
|
34
|
+
"description": "Report body language, resolved from prefs.global.outputLanguage."
|
|
35
|
+
},
|
|
36
|
+
"account": {
|
|
37
|
+
"type": ["string", "null"],
|
|
38
|
+
"description": "Account id resolved by the account picker; null for local-only flows."
|
|
39
|
+
},
|
|
40
|
+
"repos": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"minItems": 1,
|
|
43
|
+
"items": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"additionalProperties": false,
|
|
46
|
+
"required": ["name", "layer"],
|
|
47
|
+
"properties": {
|
|
48
|
+
"name": { "type": "string", "minLength": 1 },
|
|
49
|
+
"layer": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"enum": ["ios", "android", "web", "mobile-bff", "web-bff", "other"],
|
|
52
|
+
"description": "User-confirmed layer tag (Phase 0 Step 3b). core is NOT a layer here - it is the residual verdict, not a repo."
|
|
53
|
+
},
|
|
54
|
+
"localPath": { "type": ["string", "null"] },
|
|
55
|
+
"provider": { "type": ["string", "null"] }
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"description": "Repos selected in the multi-select picker, each tagged with the layer it implements."
|
|
59
|
+
},
|
|
60
|
+
"input": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"additionalProperties": false,
|
|
63
|
+
"required": ["sources", "complaintCount", "redacted"],
|
|
64
|
+
"properties": {
|
|
65
|
+
"sources": {
|
|
66
|
+
"type": "array",
|
|
67
|
+
"minItems": 1,
|
|
68
|
+
"items": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"additionalProperties": false,
|
|
71
|
+
"required": ["type"],
|
|
72
|
+
"properties": {
|
|
73
|
+
"type": { "type": "string", "enum": ["file", "paste", "jira", "confluence"] },
|
|
74
|
+
"ref": {
|
|
75
|
+
"type": ["string", "null"],
|
|
76
|
+
"description": "File path, Jira issue key, or Confluence URL. null for paste."
|
|
77
|
+
},
|
|
78
|
+
"format": {
|
|
79
|
+
"type": ["string", "null"],
|
|
80
|
+
"enum": ["csv", "xlsx", "txt", "json", "text", null]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"complaintCount": { "type": "integer", "minimum": 0 },
|
|
86
|
+
"redacted": {
|
|
87
|
+
"type": "boolean",
|
|
88
|
+
"description": "Must be true for any run that dispatches output; only --no-redact debug runs of parse-complaints.sh produce false."
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"complaints": {
|
|
93
|
+
"type": "array",
|
|
94
|
+
"items": {
|
|
95
|
+
"type": "object",
|
|
96
|
+
"additionalProperties": false,
|
|
97
|
+
"required": ["id", "textRedacted"],
|
|
98
|
+
"properties": {
|
|
99
|
+
"id": { "type": "string", "pattern": "^C-[0-9]{2,}$" },
|
|
100
|
+
"textRedacted": { "type": "string", "minLength": 1 },
|
|
101
|
+
"trxId": { "type": ["string", "null"] },
|
|
102
|
+
"convId": { "type": ["string", "null"] },
|
|
103
|
+
"date": { "type": ["string", "null"] },
|
|
104
|
+
"platformHint": { "type": ["string", "null"] },
|
|
105
|
+
"skipGraylog": {
|
|
106
|
+
"type": "boolean",
|
|
107
|
+
"default": false,
|
|
108
|
+
"description": "True when the user explicitly marked the complaint skip-graylog at the Phase 0 Step 5 id-confirmation loop."
|
|
109
|
+
},
|
|
110
|
+
"graylog": {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"additionalProperties": false,
|
|
113
|
+
"required": ["status"],
|
|
114
|
+
"properties": {
|
|
115
|
+
"status": { "type": "string", "enum": ["pending", "ok", "degraded", "skipped"] },
|
|
116
|
+
"totalResults": { "type": "integer", "minimum": 0 },
|
|
117
|
+
"degradeReason": { "type": ["string", "null"] }
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"repoEvidence": {
|
|
121
|
+
"type": "array",
|
|
122
|
+
"items": {
|
|
123
|
+
"type": "object",
|
|
124
|
+
"additionalProperties": false,
|
|
125
|
+
"required": ["repo", "layer", "file", "signal", "matchKind"],
|
|
126
|
+
"properties": {
|
|
127
|
+
"repo": { "type": "string" },
|
|
128
|
+
"layer": {
|
|
129
|
+
"type": "string",
|
|
130
|
+
"enum": ["ios", "android", "web", "mobile-bff", "web-bff", "other"]
|
|
131
|
+
},
|
|
132
|
+
"file": { "type": "string" },
|
|
133
|
+
"line": { "type": ["integer", "null"] },
|
|
134
|
+
"signal": {
|
|
135
|
+
"type": "string",
|
|
136
|
+
"description": "The Graylog-derived signal that matched: endpoint path, error code, exception name, message template, or source service name."
|
|
137
|
+
},
|
|
138
|
+
"matchKind": { "type": "string", "enum": ["direct", "partial"] }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"verdict": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"additionalProperties": false,
|
|
145
|
+
"required": ["category", "confidence", "rationale"],
|
|
146
|
+
"properties": {
|
|
147
|
+
"category": {
|
|
148
|
+
"type": "string",
|
|
149
|
+
"enum": ["client", "bff", "core", "insufficient-evidence"]
|
|
150
|
+
},
|
|
151
|
+
"layer": {
|
|
152
|
+
"type": ["string", "null"],
|
|
153
|
+
"enum": ["ios", "android", "web", "mobile-bff", "web-bff", null],
|
|
154
|
+
"description": "Set for client/bff verdicts; null for core and insufficient-evidence."
|
|
155
|
+
},
|
|
156
|
+
"confidence": { "type": "string", "enum": ["high", "medium", "low"] },
|
|
157
|
+
"rationale": { "type": "string", "minLength": 1 }
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"handoff": {
|
|
161
|
+
"type": ["object", "null"],
|
|
162
|
+
"additionalProperties": false,
|
|
163
|
+
"description": "Present only for client/bff verdicts: development handoff grounded in the existing architecture (repoEvidence files as reference).",
|
|
164
|
+
"properties": {
|
|
165
|
+
"fixPlan": {
|
|
166
|
+
"type": "array",
|
|
167
|
+
"items": { "type": "string" },
|
|
168
|
+
"description": "2-5 numbered steps, each referencing a cited file:line or component, reuse-first."
|
|
169
|
+
},
|
|
170
|
+
"devPrompt": {
|
|
171
|
+
"type": ["string", "null"],
|
|
172
|
+
"description": "English prompt block ready for /multi-agent:dev or a Jira description; includes the [C-NN] id."
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"routing": {
|
|
177
|
+
"type": ["object", "null"],
|
|
178
|
+
"additionalProperties": false,
|
|
179
|
+
"description": "Present only for core verdicts: the routing recommendation to the backend core team. Never contains fix analysis.",
|
|
180
|
+
"properties": {
|
|
181
|
+
"suspectedService": {
|
|
182
|
+
"type": ["string", "null"],
|
|
183
|
+
"description": "Graylog message source field of the failing upstream service."
|
|
184
|
+
},
|
|
185
|
+
"endpoint": { "type": ["string", "null"] },
|
|
186
|
+
"errorCode": { "type": ["string", "null"] },
|
|
187
|
+
"evidenceExcerpt": {
|
|
188
|
+
"type": ["string", "null"],
|
|
189
|
+
"description": "Short redacted English excerpt from the Graylog message."
|
|
190
|
+
},
|
|
191
|
+
"suggestedQueue": {
|
|
192
|
+
"type": ["string", "null"],
|
|
193
|
+
"description": "Optional team/queue label from prefs.projects[*].routing.coreTeamLabel; null when unset."
|
|
194
|
+
},
|
|
195
|
+
"confidence": { "type": ["string", "null"], "enum": ["high", "medium", "low", null] }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"outputs": {
|
|
202
|
+
"type": "object",
|
|
203
|
+
"additionalProperties": false,
|
|
204
|
+
"properties": {
|
|
205
|
+
"draftDir": { "type": ["string", "null"] },
|
|
206
|
+
"files": { "type": "array", "items": { "type": "string" } },
|
|
207
|
+
"destinations": {
|
|
208
|
+
"type": "array",
|
|
209
|
+
"items": { "type": "string", "enum": ["local", "confluence", "jira"] }
|
|
210
|
+
},
|
|
211
|
+
"confluencePageUrl": { "type": ["string", "null"] },
|
|
212
|
+
"jiraIssueKey": { "type": ["string", "null"] }
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* prefs-2.5.0-to-2.6.0.mjs - preference schema upgrade 2.5.0 -> 2.6.0
|
|
3
|
+
*
|
|
4
|
+
* v15.0.0 renamed the pipeline-tail command a second time: /multi-agent:finish
|
|
5
|
+
* (pre-2.5.0) became :ship (2.5.0) and is now :resume-local, because the command
|
|
6
|
+
* continues locally-done work through review/commit/report - it opens the PR but
|
|
7
|
+
* merges nothing, so "ship" oversold it. One preference key follows:
|
|
8
|
+
*
|
|
9
|
+
* - global.resumeLocal.autoFix (default FALSE)
|
|
10
|
+
* Carried across from global.ship.autoFix (itself carried from
|
|
11
|
+
* global.finish.autoFix): a user who opted into auto-fix did so
|
|
12
|
+
* deliberately, so the value survives every hop of the rename chain.
|
|
13
|
+
*
|
|
14
|
+
* Idempotent: safe on already-2.6.0 input. Non-destructive: an existing explicit
|
|
15
|
+
* value is preserved; the legacy `ship`/`finish` blocks are removed because they
|
|
16
|
+
* are no longer declared properties and would fail a closed `global`.
|
|
17
|
+
*
|
|
18
|
+
* @param {object} data - parsed JSON content of the preferences file
|
|
19
|
+
* @returns {object} - same data with schemaVersion = "2.6.0"
|
|
20
|
+
*/
|
|
21
|
+
export default function migrate(data) {
|
|
22
|
+
const out = JSON.parse(JSON.stringify(data));
|
|
23
|
+
|
|
24
|
+
out.schemaVersion = "2.6.0";
|
|
25
|
+
|
|
26
|
+
if (!out.global) out.global = {};
|
|
27
|
+
|
|
28
|
+
if (typeof out.global.resumeLocal !== "object" || out.global.resumeLocal === null) {
|
|
29
|
+
out.global.resumeLocal = {};
|
|
30
|
+
}
|
|
31
|
+
if (typeof out.global.resumeLocal.autoFix !== "boolean") {
|
|
32
|
+
const legacy =
|
|
33
|
+
typeof out.global.ship?.autoFix === "boolean"
|
|
34
|
+
? out.global.ship.autoFix
|
|
35
|
+
: out.global.finish?.autoFix;
|
|
36
|
+
out.global.resumeLocal.autoFix = typeof legacy === "boolean" ? legacy : false;
|
|
37
|
+
}
|
|
38
|
+
for (const key of ["ship", "finish"]) {
|
|
39
|
+
if (out.global[key] && typeof out.global[key] === "object") {
|
|
40
|
+
delete out.global[key].autoFix;
|
|
41
|
+
if (Object.keys(out.global[key]).length === 0) delete out.global[key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return out;
|
|
46
|
+
}
|