peaks-loop 4.0.7 → 4.0.9
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 +58 -288
- package/dist/cli/commands/_register.js +2 -0
- package/dist/cli/commands/baseline-commands.d.ts +3 -0
- package/dist/cli/commands/baseline-commands.js +146 -0
- package/dist/cli/commands/code-job-shape-commands.js +8 -11
- package/dist/cli/commands/code-mode-gate-commands.js +1 -43
- package/dist/cli/commands/code-runtime-commands.js +36 -14
- package/dist/cli/commands/core/skill-command.js +75 -13
- package/dist/cli/commands/core/standards-command.js +5 -50
- package/dist/cli/commands/dispatch-commands.d.ts +20 -0
- package/dist/cli/commands/dispatch-commands.js +39 -38
- package/dist/cli/commands/dispatch-from-dag.js +17 -0
- package/dist/cli/commands/heartbeat-commands.d.ts +21 -0
- package/dist/cli/commands/heartbeat-commands.js +40 -0
- package/dist/cli/commands/hook-handle.js +5 -2
- package/dist/cli/commands/hooks-commands.js +9 -26
- package/dist/cli/commands/loop-eval-commands.js +5 -2
- package/dist/cli/commands/statusline-commands.js +1 -1
- package/dist/cli/commands/sub-agent-commands.js +0 -2
- package/dist/cli/commands/sub-agent-shared.d.ts +7 -9
- package/dist/cli/commands/workflow-commands.js +8 -0
- package/dist/cli/commands/workflow-lifecycle-commands.d.ts +63 -0
- package/dist/cli/commands/workflow-lifecycle-commands.js +302 -0
- package/dist/cli/commands/workspace/init-command.js +19 -0
- package/dist/services/audit/backing-detector.d.ts +2 -19
- package/dist/services/audit/backing-detector.js +7 -66
- package/dist/services/audit/enforcers/active-skill-resolver.d.ts +25 -9
- package/dist/services/audit/enforcers/active-skill-resolver.js +83 -21
- package/dist/services/audit/enforcers/lint-audit-regression.js +1 -10
- package/dist/services/capability-audit-service/cross-check.d.ts +8 -0
- package/dist/services/capability-audit-service/cross-check.js +11 -0
- package/dist/services/capability-audit-service/index.d.ts +3 -0
- package/dist/services/capability-audit-service/index.js +2 -0
- package/dist/services/capability-audit-service/runner.d.ts +27 -0
- package/dist/services/capability-audit-service/runner.js +40 -0
- package/dist/services/capability-audit-service/staleness.d.ts +1 -0
- package/dist/services/capability-audit-service/staleness.js +7 -0
- package/dist/services/capability-audit-service/types.d.ts +24 -0
- package/dist/services/capability-audit-service/types.js +1 -0
- package/dist/services/capability-baseline/store.d.ts +33 -0
- package/dist/services/capability-baseline/store.js +80 -0
- package/dist/services/capability-baseline/types.d.ts +45 -0
- package/dist/services/capability-baseline/types.js +5 -0
- package/dist/services/capability-baseline/validator.d.ts +19 -0
- package/dist/services/capability-baseline/validator.js +38 -0
- package/dist/services/capability-guard-runner/contracts/J01.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J01.js +48 -0
- package/dist/services/capability-guard-runner/contracts/J02.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J02.js +34 -0
- package/dist/services/capability-guard-runner/contracts/J03.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J03.js +24 -0
- package/dist/services/capability-guard-runner/contracts/J04.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J04.js +40 -0
- package/dist/services/capability-guard-runner/contracts/J05.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J05.js +21 -0
- package/dist/services/capability-guard-runner/contracts/J06.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J06.js +43 -0
- package/dist/services/capability-guard-runner/contracts/J07.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J07.js +53 -0
- package/dist/services/capability-guard-runner/contracts/J08.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J08.js +43 -0
- package/dist/services/capability-guard-runner/contracts/J09.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J09.js +44 -0
- package/dist/services/capability-guard-runner/contracts/J10.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J10.js +40 -0
- package/dist/services/capability-guard-runner/contracts/J11.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J11.js +39 -0
- package/dist/services/capability-guard-runner/contracts/J12.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J12.js +34 -0
- package/dist/services/capability-guard-runner/contracts/J13.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J13.js +45 -0
- package/dist/services/capability-guard-runner/contracts/J14.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J14.js +36 -0
- package/dist/services/capability-guard-runner/contracts/J15.d.ts +2 -0
- package/dist/services/capability-guard-runner/contracts/J15.js +40 -0
- package/dist/services/capability-guard-runner/diff.d.ts +2 -0
- package/dist/services/capability-guard-runner/diff.js +7 -0
- package/dist/services/capability-guard-runner/runner.d.ts +9 -0
- package/dist/services/capability-guard-runner/runner.js +22 -0
- package/dist/services/capability-guard-runner/types.d.ts +39 -0
- package/dist/services/capability-guard-runner/types.js +1 -0
- package/dist/services/code/auto-compact-orchestrator.d.ts +35 -2
- package/dist/services/code/auto-compact-orchestrator.js +29 -9
- package/dist/services/context/build-dispatch-system-prompt.js +0 -18
- package/dist/services/dispatch/dispatch-record-writer.d.ts +25 -1
- package/dist/services/dispatch/dispatch-record-writer.js +85 -15
- package/dist/services/dispatch/test-tool-detection.d.ts +0 -14
- package/dist/services/dispatch/test-tool-detection.js +0 -22
- package/dist/services/doctor/doctor-service/checks/skill-presence.d.ts +8 -0
- package/dist/services/doctor/doctor-service/checks/skill-presence.js +9 -1
- package/dist/services/evolution/evolution-types.d.ts +16 -16
- package/dist/services/final-review/final-review-service.d.ts +8 -0
- package/dist/services/final-review/final-review-service.js +14 -0
- package/dist/services/final-review/final-review-types.d.ts +1 -1
- package/dist/services/hooks/presence-marker-detector.d.ts +6 -0
- package/dist/services/hooks/presence-marker-detector.js +48 -0
- package/dist/services/ide/adapters/claude-code-adapter.js +20 -0
- package/dist/services/ide/adapters/codex-adapter.js +20 -1
- package/dist/services/ide/adapters/cursor-adapter.js +21 -1
- package/dist/services/ide/adapters/hermes-adapter.js +20 -1
- package/dist/services/ide/adapters/openclaw-adapter.js +20 -1
- package/dist/services/ide/adapters/qoder-adapter.js +20 -1
- package/dist/services/ide/adapters/tongyi-lingma-adapter.js +20 -1
- package/dist/services/ide/adapters/trae-adapter.js +21 -1
- package/dist/services/ide/adapters/zcode-adapter.js +19 -0
- package/dist/services/ide/ide-types.d.ts +15 -0
- package/dist/services/job/job-progress-store.d.ts +2 -2
- package/dist/services/job/job-types.d.ts +26 -26
- package/dist/services/observability/observability-service.d.ts +4 -4
- package/dist/services/scan/archetype-service.js +2 -52
- package/dist/services/sediment/json-schema.d.ts +4 -4
- package/dist/services/session/caller-binding-service.d.ts +21 -0
- package/dist/services/session/caller-binding-service.js +35 -0
- package/dist/services/session/caller-id-types.d.ts +62 -13
- package/dist/services/session/caller-id-types.js +25 -13
- package/dist/services/session/index.d.ts +4 -2
- package/dist/services/session/index.js +8 -2
- package/dist/services/session/platform-fallbacks.d.ts +25 -17
- package/dist/services/session/platform-fallbacks.js +26 -28
- package/dist/services/session/resolve-caller-id.d.ts +64 -29
- package/dist/services/session/resolve-caller-id.js +122 -48
- package/dist/services/session/session-manager.d.ts +0 -29
- package/dist/services/session/session-manager.js +0 -33
- package/dist/services/session/session-resume-service.d.ts +4 -0
- package/dist/services/session/session-resume-service.js +4 -0
- package/dist/services/skills/hooks-settings-service.d.ts +0 -41
- package/dist/services/skills/hooks-settings-service.js +5 -61
- package/dist/services/skills/presence-lease-service.d.ts +73 -0
- package/dist/services/skills/presence-lease-service.js +358 -0
- package/dist/services/skills/presence-lease-types.d.ts +75 -0
- package/dist/services/skills/presence-lease-types.js +16 -0
- package/dist/services/skills/skill-presence-service.js +86 -22
- package/dist/services/skills/skill-statusline-renderer.d.ts +46 -56
- package/dist/services/skills/skill-statusline-renderer.js +312 -100
- package/dist/services/slice/slice-check-service.js +0 -59
- package/dist/services/standards/project-standards-service.js +0 -44
- package/dist/services/workflow/workflow-graph-store.d.ts +77 -0
- package/dist/services/workflow/workflow-graph-store.js +278 -0
- package/dist/services/workflow/workflow-graph-types.d.ts +56 -0
- package/dist/services/workflow/workflow-graph-types.js +67 -0
- package/dist/services/workflow/workflow-inflight-probe.d.ts +52 -0
- package/dist/services/workflow/workflow-inflight-probe.js +86 -0
- package/dist/services/workflow/workflow-node-lifecycle.d.ts +67 -0
- package/dist/services/workflow/workflow-node-lifecycle.js +348 -0
- package/dist/services/workflow/workflow-presence-lifecycle.d.ts +58 -0
- package/dist/services/workflow/workflow-presence-lifecycle.js +196 -0
- package/dist/services/workspace/reconcile-service.d.ts +31 -0
- package/dist/services/workspace/reconcile-service.js +132 -1
- package/package.json +5 -4
- package/skills/peaks-code/SKILL.md +5 -43
- package/skills/peaks-code/references/completion-handoff.md +1 -1
- package/skills/peaks-code/references/skill-presence-and-title.md +1 -1
- package/dist/cli/commands/sub-agent-exec-command.d.ts +0 -25
- package/dist/cli/commands/sub-agent-exec-command.js +0 -132
- package/dist/services/standards/find-skills-integration.d.ts +0 -59
- package/dist/services/standards/find-skills-integration.js +0 -225
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slice 4.0.7-PR-12 (ice-cola surface probe 2026-08-02).
|
|
3
|
-
*
|
|
4
|
-
* After `peaks standards init` / `peaks standards update` materializes
|
|
5
|
-
* the project-local standards tree, the user often wants to also
|
|
6
|
-
* bring in matching skills from the open agent-skills ecosystem
|
|
7
|
-
* (https://skills.sh/). A TypeScript monorepo with React + NestJS +
|
|
8
|
-
* Tailwind + Playwright + pnpm has natural companions in
|
|
9
|
-
* vercel-labs/agent-skills, anthropics/skills, and similar curated
|
|
10
|
-
* collections.
|
|
11
|
-
*
|
|
12
|
-
* Slice 4.0.7 dogfood round 2 (user direction 2026-08-02): do NOT
|
|
13
|
-
* hardcode a `VERIFIED_SKILL_OWNERS` whitelist. The first version
|
|
14
|
-
* shipped a static list of "trusted" owners, but that is exactly
|
|
15
|
-
* what the find-skills skill tells users NOT to do — let the
|
|
16
|
-
* registry + leaderboard decide. This service now:
|
|
17
|
-
* 1. maps the detected ProjectContext to a small set of natural
|
|
18
|
-
* language queries (component library, build tool, language).
|
|
19
|
-
* 2. runs `npx skills find <query>` ONCE per query with no
|
|
20
|
-
* `--owner` filter, so the registry returns whatever the
|
|
21
|
-
* leaderboard surfaces (vercel-labs/agent-skills at the top
|
|
22
|
-
* for "react" is a registry result, not a hardcoded one).
|
|
23
|
-
* 3. parses + dedups + ranks by install count desc (the
|
|
24
|
-
* registry / leaderboard already orders by popularity).
|
|
25
|
-
* 4. emits a `SkillRecommendation` list with `install` command.
|
|
26
|
-
*
|
|
27
|
-
* **Hard rule (per peaks-loop external-references):** this service
|
|
28
|
-
* NEVER calls `npx skills add` itself. Installation is gated on the
|
|
29
|
-
* user passing `--suggest-skills` to `peaks standards init/update`,
|
|
30
|
-
* and the install command is emitted as a single shell snippet the
|
|
31
|
-
* user can review before pasting. Default behavior: recommendation
|
|
32
|
-
* only, no install. The CLI caller owns the gating.
|
|
33
|
-
*
|
|
34
|
-
* Pure: no I/O of the project tree; only `npx skills find` calls
|
|
35
|
-
* (which are external side effects that we treat as testable via a
|
|
36
|
-
* `skillFindRunner` injection).
|
|
37
|
-
*/
|
|
38
|
-
import { spawn } from 'node:child_process';
|
|
39
|
-
/**
|
|
40
|
-
* Map a ProjectContext to a small query set. Returns at most 8
|
|
41
|
-
* queries to keep the network surface bounded; ordering is
|
|
42
|
-
* best-to-worst (more specific first, then broad). Deliberately
|
|
43
|
-
* no hardcoded owner whitelist — `findSkillsForContext` runs
|
|
44
|
-
* `npx skills find <query>` with no `--owner` filter so the
|
|
45
|
-
* registry / leaderboard surfaces whatever the user might want
|
|
46
|
-
* (per find-skills SKILL.md Step 2-3 guidance).
|
|
47
|
-
*/
|
|
48
|
-
export function queriesForContext(ctx) {
|
|
49
|
-
const queries = [];
|
|
50
|
-
// Component library first (most specific)
|
|
51
|
-
if (ctx.componentLibrary.name === 'antd')
|
|
52
|
-
queries.push('antd', 'react');
|
|
53
|
-
else if (ctx.componentLibrary.name === 'antd-pro')
|
|
54
|
-
queries.push('antd-pro', 'react', 'pro-components');
|
|
55
|
-
else if (ctx.componentLibrary.name === 'mui')
|
|
56
|
-
queries.push('mui', 'react');
|
|
57
|
-
else if (ctx.componentLibrary.name === 'shadcn')
|
|
58
|
-
queries.push('shadcn', 'react', 'tailwind');
|
|
59
|
-
else if (ctx.componentLibrary.name === 'arco')
|
|
60
|
-
queries.push('arco', 'react');
|
|
61
|
-
else if (ctx.componentLibrary.name === 'tdesign')
|
|
62
|
-
queries.push('tdesign', 'react');
|
|
63
|
-
else if (ctx.componentLibrary.name === 'element-plus')
|
|
64
|
-
queries.push('element-plus', 'vue');
|
|
65
|
-
else if (ctx.componentLibrary.name === 'vant')
|
|
66
|
-
queries.push('vant', 'mobile');
|
|
67
|
-
// Build tool
|
|
68
|
-
if (ctx.buildTool === 'next')
|
|
69
|
-
queries.push('nextjs', 'react');
|
|
70
|
-
else if (ctx.buildTool === 'vite')
|
|
71
|
-
queries.push('vite', 'frontend');
|
|
72
|
-
else if (ctx.buildTool === 'umi')
|
|
73
|
-
queries.push('umi');
|
|
74
|
-
else if (ctx.buildTool === 'rsbuild')
|
|
75
|
-
queries.push('rsbuild', 'rspack');
|
|
76
|
-
else if (ctx.buildTool === 'rspack')
|
|
77
|
-
queries.push('rspack');
|
|
78
|
-
// CSS framework
|
|
79
|
-
if (ctx.cssFrameworks.includes('tailwind'))
|
|
80
|
-
queries.push('tailwind');
|
|
81
|
-
if (ctx.cssFrameworks.includes('less'))
|
|
82
|
-
queries.push('less');
|
|
83
|
-
// Domain hints
|
|
84
|
-
for (const dep of ctx.notableDeps) {
|
|
85
|
-
if (dep.startsWith('@nestjs/'))
|
|
86
|
-
queries.push('nestjs');
|
|
87
|
-
if (dep === 'playwright' || dep === '@playwright/test')
|
|
88
|
-
queries.push('playwright', 'e2e');
|
|
89
|
-
if (dep === 'vitest')
|
|
90
|
-
queries.push('vitest', 'testing');
|
|
91
|
-
if (dep === 'prisma')
|
|
92
|
-
queries.push('prisma');
|
|
93
|
-
if (dep === 'drizzle-orm')
|
|
94
|
-
queries.push('drizzle');
|
|
95
|
-
if (dep === 'graphql' || dep === '@apollo/client')
|
|
96
|
-
queries.push('graphql');
|
|
97
|
-
}
|
|
98
|
-
// Always include the language + the platform itself
|
|
99
|
-
queries.push(ctx.hasPackageJson ? 'typescript' : 'javascript');
|
|
100
|
-
// Dedup while preserving order, then cap at 12 to bound the
|
|
101
|
-
// network surface (each query fans out to the registry). 12
|
|
102
|
-
// keeps both "specific" (antd, react, nestjs) and "broad"
|
|
103
|
-
// (typescript / javascript) queries in the same list.
|
|
104
|
-
const seen = new Set();
|
|
105
|
-
const deduped = queries.filter((q) => (seen.has(q) ? false : (seen.add(q), true)));
|
|
106
|
-
return deduped.slice(0, 12);
|
|
107
|
-
}
|
|
108
|
-
export const defaultSkillFindRunner = (query) => new Promise((resolveText, reject) => {
|
|
109
|
-
// No `--owner` flag: the registry's leaderboard decides the
|
|
110
|
-
// ranking. We do NOT hardcode a "verified owners" whitelist —
|
|
111
|
-
// that is exactly the anti-pattern the find-skills SKILL.md
|
|
112
|
-
// warns against (Step 4: verify install count + source
|
|
113
|
-
// reputation, but trust the leaderboard first).
|
|
114
|
-
const child = spawn('npx', ['skills', 'find', query], {
|
|
115
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
116
|
-
});
|
|
117
|
-
let stdout = '';
|
|
118
|
-
let stderr = '';
|
|
119
|
-
child.stdout.on('data', (chunk) => { stdout += chunk.toString('utf8'); });
|
|
120
|
-
child.stderr.on('data', (chunk) => { stderr += chunk.toString('utf8'); });
|
|
121
|
-
child.on('error', reject);
|
|
122
|
-
child.on('close', (code) => {
|
|
123
|
-
if (code === 0)
|
|
124
|
-
resolveText(stdout);
|
|
125
|
-
else
|
|
126
|
-
reject(new Error(`npx skills find exited ${code}: ${stderr.slice(0, 200)}`));
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
/**
|
|
130
|
-
* Parse a `npx skills find` stdout blob into SkillRecommendation rows.
|
|
131
|
-
* The CLI output is line-oriented; a row looks like
|
|
132
|
-
* <owner>/<repo>@<slug> <install-count> <description>
|
|
133
|
-
* The parser is lenient — any line that contains a
|
|
134
|
-
* `<owner>/<repo>@<slug>` triple is captured.
|
|
135
|
-
*/
|
|
136
|
-
export function parseFindOutput(stdout) {
|
|
137
|
-
const lines = stdout.split('\n');
|
|
138
|
-
const rows = [];
|
|
139
|
-
const rowRe = /([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)@([A-Za-z0-9_.-]+)/;
|
|
140
|
-
for (const line of lines) {
|
|
141
|
-
const m = line.match(rowRe);
|
|
142
|
-
if (!m || m[1] === undefined || m[2] === undefined || m[3] === undefined)
|
|
143
|
-
continue;
|
|
144
|
-
const rowOwner = m[1];
|
|
145
|
-
const repoName = m[2];
|
|
146
|
-
const skillSlug = m[3];
|
|
147
|
-
// Parse install count if the line has a number with K/M suffix.
|
|
148
|
-
let installCount = null;
|
|
149
|
-
const countMatch = line.match(/(\d+(?:\.\d+)?)\s*([KkMm]?)\b/);
|
|
150
|
-
if (countMatch && countMatch[1] !== undefined) {
|
|
151
|
-
const base = parseFloat(countMatch[1]);
|
|
152
|
-
const suffix = (countMatch[2] ?? '').toUpperCase();
|
|
153
|
-
if (!Number.isNaN(base)) {
|
|
154
|
-
installCount = suffix === 'K' ? base * 1_000 : suffix === 'M' ? base * 1_000_000 : base;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
const desc = line.replace(rowRe, '').trim();
|
|
158
|
-
const fullSlug = `${rowOwner}/${repoName}@${skillSlug}`;
|
|
159
|
-
rows.push({
|
|
160
|
-
owner: rowOwner,
|
|
161
|
-
skillSlug,
|
|
162
|
-
displayName: skillSlug,
|
|
163
|
-
description: desc,
|
|
164
|
-
installCount,
|
|
165
|
-
installCommand: `npx skills add ${fullSlug} -g -y`,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
return rows;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Main entry: query the registry for each query, dedup, sort by
|
|
172
|
-
* install-count desc (the registry's leaderboard already orders by
|
|
173
|
-
* popularity; we keep the same ordering). Returns top-N rows.
|
|
174
|
-
*/
|
|
175
|
-
export async function findSkillsForContext(ctx, options = {}) {
|
|
176
|
-
const topN = options.topN ?? 8;
|
|
177
|
-
const findRunner = options.findRunner ?? defaultSkillFindRunner;
|
|
178
|
-
const queries = queriesForContext(ctx);
|
|
179
|
-
const seen = new Set();
|
|
180
|
-
const all = [];
|
|
181
|
-
const emptyQueries = [];
|
|
182
|
-
const failedRuns = [];
|
|
183
|
-
for (const query of queries) {
|
|
184
|
-
try {
|
|
185
|
-
const stdout = await findRunner(query);
|
|
186
|
-
const rows = parseFindOutput(stdout);
|
|
187
|
-
if (rows.length === 0) {
|
|
188
|
-
emptyQueries.push(query);
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
for (const r of rows) {
|
|
192
|
-
const key = `${r.owner}/${r.skillSlug}`;
|
|
193
|
-
if (seen.has(key))
|
|
194
|
-
continue;
|
|
195
|
-
seen.add(key);
|
|
196
|
-
all.push(r);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
catch (err) {
|
|
200
|
-
failedRuns.push({
|
|
201
|
-
query,
|
|
202
|
-
reason: err instanceof Error ? err.message : String(err),
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
// Sort by install count desc (registry leaderboard is already
|
|
207
|
-
// ordered by popularity; we keep the same order so the user sees
|
|
208
|
-
// the same top results they would see in `npx skills find`).
|
|
209
|
-
// Tiebreaker: display name asc.
|
|
210
|
-
all.sort((a, b) => {
|
|
211
|
-
if (a.installCount !== null && b.installCount !== null && a.installCount !== b.installCount) {
|
|
212
|
-
return b.installCount - a.installCount;
|
|
213
|
-
}
|
|
214
|
-
if (a.installCount === null && b.installCount !== null)
|
|
215
|
-
return 1;
|
|
216
|
-
if (a.installCount !== null && b.installCount === null)
|
|
217
|
-
return -1;
|
|
218
|
-
return a.displayName.localeCompare(b.displayName);
|
|
219
|
-
});
|
|
220
|
-
return {
|
|
221
|
-
recommendations: all.slice(0, topN),
|
|
222
|
-
emptyQueries,
|
|
223
|
-
failedRuns,
|
|
224
|
-
};
|
|
225
|
-
}
|