@phren/cli 0.0.10 → 0.0.12
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/README.md +11 -17
- package/mcp/dist/capabilities/cli.js +1 -1
- package/mcp/dist/capabilities/mcp.js +1 -1
- package/mcp/dist/capabilities/vscode.js +1 -1
- package/mcp/dist/capabilities/web-ui.js +1 -1
- package/mcp/dist/cli-actions.js +58 -71
- package/mcp/dist/cli-config.js +337 -131
- package/mcp/dist/cli-extract.js +3 -2
- package/mcp/dist/cli-govern.js +35 -63
- package/mcp/dist/cli-graph.js +19 -4
- package/mcp/dist/cli-hooks-globs.js +2 -1
- package/mcp/dist/cli-hooks-output.js +4 -4
- package/mcp/dist/cli-hooks-session.js +1 -1
- package/mcp/dist/cli-hooks.js +44 -35
- package/mcp/dist/cli-namespaces.js +15 -5
- package/mcp/dist/cli-search.js +2 -2
- package/mcp/dist/cli.js +1 -1
- package/mcp/dist/content-archive.js +23 -14
- package/mcp/dist/content-citation.js +13 -2
- package/mcp/dist/content-dedup.js +9 -9
- package/mcp/dist/content-learning.js +6 -4
- package/mcp/dist/content-metadata.js +10 -0
- package/mcp/dist/core-finding.js +1 -1
- package/mcp/dist/data-access.js +10 -31
- package/mcp/dist/data-tasks.js +5 -26
- package/mcp/dist/embedding.js +7 -8
- package/mcp/dist/entrypoint.js +133 -102
- package/mcp/dist/finding-impact.js +1 -32
- package/mcp/dist/finding-journal.js +1 -1
- package/mcp/dist/finding-lifecycle.js +2 -7
- package/mcp/dist/governance-locks.js +12 -5
- package/mcp/dist/governance-policy.js +156 -9
- package/mcp/dist/governance-scores.js +4 -10
- package/mcp/dist/hooks.js +62 -18
- package/mcp/dist/index.js +4 -4
- package/mcp/dist/init-config.js +4 -25
- package/mcp/dist/init-preferences.js +1 -1
- package/mcp/dist/init-setup.js +6 -55
- package/mcp/dist/init-shared.js +53 -1
- package/mcp/dist/init.js +191 -29
- package/mcp/dist/link-checksums.js +3 -2
- package/mcp/dist/link-context.js +2 -2
- package/mcp/dist/link-doctor.js +14 -57
- package/mcp/dist/link-skills.js +98 -12
- package/mcp/dist/link.js +16 -75
- package/mcp/dist/machine-identity.js +1 -9
- package/mcp/dist/mcp-config.js +247 -42
- package/mcp/dist/mcp-data.js +9 -9
- package/mcp/dist/mcp-extract-facts.js +12 -7
- package/mcp/dist/mcp-extract.js +2 -2
- package/mcp/dist/mcp-finding.js +16 -20
- package/mcp/dist/mcp-graph.js +12 -12
- package/mcp/dist/mcp-hooks.js +1 -1
- package/mcp/dist/mcp-ops.js +18 -18
- package/mcp/dist/mcp-search.js +11 -16
- package/mcp/dist/mcp-session.js +12 -2
- package/mcp/dist/memory-ui-assets.js +1 -36
- package/mcp/dist/memory-ui-graph.js +152 -50
- package/mcp/dist/memory-ui-page.js +30 -5
- package/mcp/dist/memory-ui-scripts.js +252 -63
- package/mcp/dist/memory-ui-server.js +115 -3
- package/mcp/dist/phren-core.js +2 -0
- package/mcp/dist/phren-paths.js +8 -9
- package/mcp/dist/proactivity.js +5 -5
- package/mcp/dist/profile-store.js +2 -2
- package/mcp/dist/project-config.js +64 -17
- package/mcp/dist/provider-adapters.js +1 -1
- package/mcp/dist/query-correlation.js +22 -19
- package/mcp/dist/session-checkpoints.js +14 -14
- package/mcp/dist/session-utils.js +3 -2
- package/mcp/dist/shared-data-utils.js +28 -0
- package/mcp/dist/shared-fragment-graph.js +22 -21
- package/mcp/dist/shared-governance.js +1 -1
- package/mcp/dist/shared-index.js +144 -105
- package/mcp/dist/shared-retrieval.js +21 -23
- package/mcp/dist/shared-search-fallback.js +15 -25
- package/mcp/dist/shared-sqljs.js +3 -2
- package/mcp/dist/shared.js +5 -6
- package/mcp/dist/shell-entry.js +1 -1
- package/mcp/dist/shell-input.js +63 -53
- package/mcp/dist/shell-palette.js +6 -1
- package/mcp/dist/shell-render.js +9 -5
- package/mcp/dist/shell-state-store.js +2 -5
- package/mcp/dist/shell-view.js +7 -6
- package/mcp/dist/shell.js +5 -55
- package/mcp/dist/skill-files.js +4 -10
- package/mcp/dist/skill-registry.js +3 -0
- package/mcp/dist/status.js +43 -21
- package/mcp/dist/task-hygiene.js +1 -1
- package/mcp/dist/telemetry.js +5 -4
- package/mcp/dist/update.js +1 -1
- package/mcp/dist/utils.js +4 -4
- package/package.json +2 -3
- package/skills/docs.md +11 -11
- package/starter/README.md +1 -1
- package/starter/global/CLAUDE.md +2 -2
- package/starter/global/skills/audit.md +106 -0
- package/mcp/dist/cli-hooks-retrieval.js +0 -2
- package/mcp/dist/impact-scoring.js +0 -22
- package/mcp/dist/shared-paths.js +0 -1
|
@@ -4,11 +4,11 @@ import { queryDocRows, queryRows, cosineFallback, extractSnippet, getDocSourceKe
|
|
|
4
4
|
import { filterTrustedFindingsDetailed, } from "./shared-content.js";
|
|
5
5
|
import { parseCitationComment } from "./content-citation.js";
|
|
6
6
|
import { getHighImpactFindings } from "./finding-impact.js";
|
|
7
|
-
import { buildFtsQueryVariants, buildRelaxedFtsQuery, isFeatureEnabled, STOP_WORDS } from "./utils.js";
|
|
7
|
+
import { buildFtsQueryVariants, buildRelaxedFtsQuery, isFeatureEnabled, STOP_WORDS, errorMessage } from "./utils.js";
|
|
8
8
|
import * as fs from "fs";
|
|
9
9
|
import * as path from "path";
|
|
10
10
|
import { getProjectGlobBoost } from "./cli-hooks-globs.js";
|
|
11
|
-
import { vectorFallback } from "./shared-search-fallback.js";
|
|
11
|
+
import { vectorFallback, deterministicSeed } from "./shared-search-fallback.js";
|
|
12
12
|
import { getOllamaUrl, getCloudEmbeddingUrl } from "./shared-ollama.js";
|
|
13
13
|
import { keywordFallbackSearch } from "./core-search.js";
|
|
14
14
|
import { debugLog } from "./shared.js";
|
|
@@ -34,17 +34,23 @@ const TASK_RESCUE_SCORE_MARGIN = 0.6;
|
|
|
34
34
|
/** Fraction of bullets that must be low-value before applying the low-value penalty. */
|
|
35
35
|
const LOW_VALUE_BULLET_FRACTION = 0.5;
|
|
36
36
|
// ── Intent and scoring helpers ───────────────────────────────────────────────
|
|
37
|
+
const INTENT_SKILL_CMD_RE = /(?:^|\s)\/(?!(?:home|usr|var|tmp|etc|opt|api|mnt)\b)[a-z][\w-]*\b/;
|
|
38
|
+
const INTENT_SKILL_KW_RE = /\bskill\b/;
|
|
39
|
+
const INTENT_DEBUG_RE = /(bug|error|fix|broken|regression|fail|stack trace)/;
|
|
40
|
+
const INTENT_REVIEW_RE = /(review|audit|pr|pull request|nit|refactor)/;
|
|
41
|
+
const INTENT_BUILD_RE = /(build|deploy|release|ci|workflow|pipeline|test)/;
|
|
42
|
+
const INTENT_DOCS_RE = /\b(doc|docs|readme|explain|guide|instructions?)\b/;
|
|
37
43
|
export function detectTaskIntent(prompt) {
|
|
38
44
|
const p = prompt.toLowerCase();
|
|
39
|
-
if (
|
|
45
|
+
if (INTENT_SKILL_CMD_RE.test(p) || INTENT_SKILL_KW_RE.test(p))
|
|
40
46
|
return "skill";
|
|
41
|
-
if (
|
|
47
|
+
if (INTENT_DEBUG_RE.test(p))
|
|
42
48
|
return "debug";
|
|
43
|
-
if (
|
|
49
|
+
if (INTENT_REVIEW_RE.test(p))
|
|
44
50
|
return "review";
|
|
45
|
-
if (
|
|
51
|
+
if (INTENT_BUILD_RE.test(p))
|
|
46
52
|
return "build";
|
|
47
|
-
if (
|
|
53
|
+
if (INTENT_DOCS_RE.test(p))
|
|
48
54
|
return "docs";
|
|
49
55
|
return "general";
|
|
50
56
|
}
|
|
@@ -155,14 +161,6 @@ function docOverlapScore(queryTokens, doc) {
|
|
|
155
161
|
const corpus = `${doc.project} ${doc.filename} ${doc.type} ${doc.path}\n${doc.content.slice(0, 5000)}`;
|
|
156
162
|
return overlapScore(queryTokens, corpus);
|
|
157
163
|
}
|
|
158
|
-
function semanticFallbackSeed(text) {
|
|
159
|
-
let hash = 2166136261;
|
|
160
|
-
for (let i = 0; i < text.length; i++) {
|
|
161
|
-
hash ^= text.charCodeAt(i);
|
|
162
|
-
hash = Math.imul(hash, 16777619);
|
|
163
|
-
}
|
|
164
|
-
return hash >>> 0;
|
|
165
|
-
}
|
|
166
164
|
function loadSemanticFallbackWindow(db, startRowid, limit, project, wrapBefore) {
|
|
167
165
|
const where = [
|
|
168
166
|
project ? "project = ?" : "",
|
|
@@ -244,7 +242,7 @@ function semanticFallbackDocs(db, prompt, project) {
|
|
|
244
242
|
const windowCount = Math.min(SEMANTIC_FALLBACK_WINDOW_COUNT, cappedLimit);
|
|
245
243
|
const perWindow = Math.max(1, Math.ceil(cappedLimit / windowCount));
|
|
246
244
|
const stride = Math.max(1, Math.floor(span / windowCount));
|
|
247
|
-
const seed =
|
|
245
|
+
const seed = deterministicSeed(`${project ?? "*"}\n${terms.join(" ")}`);
|
|
248
246
|
for (let i = 0; i < windowCount && docs.length < cappedLimit; i++) {
|
|
249
247
|
const offset = (seed + i * stride) % span;
|
|
250
248
|
const startRowid = minRowid + offset;
|
|
@@ -385,8 +383,8 @@ export async function searchDocumentsAsync(db, safeQuery, prompt, keywords, dete
|
|
|
385
383
|
}
|
|
386
384
|
catch (err) {
|
|
387
385
|
// Vector search failure is non-fatal — return sync result
|
|
388
|
-
if ((process.env.PHREN_DEBUG
|
|
389
|
-
process.stderr.write(`[phren] hybridSearch vectorFallback: ${
|
|
386
|
+
if ((process.env.PHREN_DEBUG))
|
|
387
|
+
process.stderr.write(`[phren] hybridSearch vectorFallback: ${errorMessage(err)}\n`);
|
|
390
388
|
return syncResult;
|
|
391
389
|
}
|
|
392
390
|
}
|
|
@@ -445,7 +443,7 @@ export async function searchKnowledgeRows(db, options) {
|
|
|
445
443
|
}
|
|
446
444
|
}
|
|
447
445
|
catch (err) {
|
|
448
|
-
debugLog(`rowid dedup query failed: ${
|
|
446
|
+
debugLog(`rowid dedup query failed: ${errorMessage(err)}`);
|
|
449
447
|
}
|
|
450
448
|
const cosineResults = cosineFallback(db, query, ftsRowids, maxResults - rows.length)
|
|
451
449
|
.filter((doc) => (!filterProject || doc.project === filterProject) && (!filterType || doc.type === filterType));
|
|
@@ -485,8 +483,8 @@ export async function searchKnowledgeRows(db, options) {
|
|
|
485
483
|
}
|
|
486
484
|
}
|
|
487
485
|
catch (err) {
|
|
488
|
-
if ((process.env.PHREN_DEBUG
|
|
489
|
-
process.stderr.write(`[phren] vectorFallback: ${
|
|
486
|
+
if ((process.env.PHREN_DEBUG)) {
|
|
487
|
+
process.stderr.write(`[phren] vectorFallback: ${errorMessage(err)}\n`);
|
|
490
488
|
}
|
|
491
489
|
}
|
|
492
490
|
}
|
|
@@ -732,8 +730,8 @@ export function markStaleCitations(snippet) {
|
|
|
732
730
|
}
|
|
733
731
|
}
|
|
734
732
|
catch (err) {
|
|
735
|
-
if ((process.env.PHREN_DEBUG
|
|
736
|
-
process.stderr.write(`[phren] applyCitationAnnotations fileRead: ${
|
|
733
|
+
if ((process.env.PHREN_DEBUG))
|
|
734
|
+
process.stderr.write(`[phren] applyCitationAnnotations fileRead: ${errorMessage(err)}\n`);
|
|
737
735
|
stale = true;
|
|
738
736
|
}
|
|
739
737
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "crypto";
|
|
2
2
|
import { debugLog } from "./shared.js";
|
|
3
|
-
import { STOP_WORDS } from "./utils.js";
|
|
3
|
+
import { STOP_WORDS, errorMessage } from "./utils.js";
|
|
4
4
|
import { porterStem } from "./shared-stemmer.js";
|
|
5
5
|
import { classifyFile, normalizeIndexedContent, rowToDocWithRowid } from "./shared-index.js";
|
|
6
6
|
import { embedText, cosineSimilarity, getEmbeddingModel, getOllamaUrl, getCloudEmbeddingUrl } from "./shared-ollama.js";
|
|
@@ -72,7 +72,7 @@ function cachedTokenize(text) {
|
|
|
72
72
|
tokenCache.set(key, tokens);
|
|
73
73
|
return tokens;
|
|
74
74
|
}
|
|
75
|
-
function deterministicSeed(text) {
|
|
75
|
+
export function deterministicSeed(text) {
|
|
76
76
|
let hash = 2166136261;
|
|
77
77
|
for (let i = 0; i < text.length; i++) {
|
|
78
78
|
hash ^= text.charCodeAt(i);
|
|
@@ -153,18 +153,8 @@ function tfidfCosine(docs, query, corpusN) {
|
|
|
153
153
|
return termTf * idf;
|
|
154
154
|
});
|
|
155
155
|
}
|
|
156
|
-
function cosine(a, b) {
|
|
157
|
-
let dot = 0, normA = 0, normB = 0;
|
|
158
|
-
for (let i = 0; i < a.length; i++) {
|
|
159
|
-
dot += a[i] * b[i];
|
|
160
|
-
normA += a[i] * a[i];
|
|
161
|
-
normB += b[i] * b[i];
|
|
162
|
-
}
|
|
163
|
-
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
164
|
-
return denom === 0 ? 0 : dot / denom;
|
|
165
|
-
}
|
|
166
156
|
const queryVec = buildVector(queryTokens);
|
|
167
|
-
return docTokenLists.map(docTokens =>
|
|
157
|
+
return docTokenLists.map(docTokens => cosineSimilarity(queryVec, buildVector(docTokens)));
|
|
168
158
|
}
|
|
169
159
|
/**
|
|
170
160
|
* Cosine fallback search: when FTS5 returns fewer than COSINE_FALLBACK_THRESHOLD results,
|
|
@@ -191,8 +181,8 @@ export function cosineFallback(db, query, excludeRowids, limit) {
|
|
|
191
181
|
}
|
|
192
182
|
}
|
|
193
183
|
catch (err) {
|
|
194
|
-
if ((process.env.PHREN_DEBUG
|
|
195
|
-
process.stderr.write(`[phren] cosineFallback count: ${
|
|
184
|
+
if ((process.env.PHREN_DEBUG))
|
|
185
|
+
process.stderr.write(`[phren] cosineFallback count: ${errorMessage(err)}\n`);
|
|
196
186
|
return [];
|
|
197
187
|
}
|
|
198
188
|
if (totalDocs > COSINE_MAX_CORPUS) {
|
|
@@ -220,8 +210,8 @@ export function cosineFallback(db, query, excludeRowids, limit) {
|
|
|
220
210
|
ftsRows.push(...ftsRes[0].values);
|
|
221
211
|
}
|
|
222
212
|
catch (err) {
|
|
223
|
-
if ((process.env.PHREN_DEBUG
|
|
224
|
-
process.stderr.write(`[phren] cosineFallback FTS pre-filter: ${
|
|
213
|
+
if ((process.env.PHREN_DEBUG))
|
|
214
|
+
process.stderr.write(`[phren] cosineFallback FTS pre-filter: ${errorMessage(err)}\n`);
|
|
225
215
|
}
|
|
226
216
|
}
|
|
227
217
|
// If FTS gave fewer than cap, supplement with deterministic rowid windows.
|
|
@@ -258,8 +248,8 @@ export function cosineFallback(db, query, excludeRowids, limit) {
|
|
|
258
248
|
}
|
|
259
249
|
}
|
|
260
250
|
catch (err) {
|
|
261
|
-
if ((process.env.PHREN_DEBUG
|
|
262
|
-
process.stderr.write(`[phren] cosineFallback deterministicSample: ${
|
|
251
|
+
if ((process.env.PHREN_DEBUG))
|
|
252
|
+
process.stderr.write(`[phren] cosineFallback deterministicSample: ${errorMessage(err)}\n`);
|
|
263
253
|
}
|
|
264
254
|
}
|
|
265
255
|
if (ftsRows.length === 0)
|
|
@@ -269,8 +259,8 @@ export function cosineFallback(db, query, excludeRowids, limit) {
|
|
|
269
259
|
}
|
|
270
260
|
}
|
|
271
261
|
catch (err) {
|
|
272
|
-
if ((process.env.PHREN_DEBUG
|
|
273
|
-
process.stderr.write(`[phren] cosineFallback loadDocs: ${
|
|
262
|
+
if ((process.env.PHREN_DEBUG))
|
|
263
|
+
process.stderr.write(`[phren] cosineFallback loadDocs: ${errorMessage(err)}\n`);
|
|
274
264
|
return [];
|
|
275
265
|
}
|
|
276
266
|
// Separate rowids, DocRows, and content strings for scoring
|
|
@@ -315,8 +305,8 @@ export async function vectorFallback(phrenPath, query, excludePaths, limit, proj
|
|
|
315
305
|
await cache.load();
|
|
316
306
|
}
|
|
317
307
|
catch (err) {
|
|
318
|
-
if ((process.env.PHREN_DEBUG
|
|
319
|
-
process.stderr.write(`[phren] vectorFallback cacheLoad: ${
|
|
308
|
+
if ((process.env.PHREN_DEBUG))
|
|
309
|
+
process.stderr.write(`[phren] vectorFallback cacheLoad: ${errorMessage(err)}\n`);
|
|
320
310
|
}
|
|
321
311
|
}
|
|
322
312
|
if (cache.size() === 0)
|
|
@@ -367,8 +357,8 @@ export async function vectorFallback(phrenPath, query, excludePaths, limit, proj
|
|
|
367
357
|
}
|
|
368
358
|
}
|
|
369
359
|
catch (err) {
|
|
370
|
-
if ((process.env.PHREN_DEBUG
|
|
371
|
-
process.stderr.write(`[phren] vectorFallback fileRead: ${
|
|
360
|
+
if ((process.env.PHREN_DEBUG))
|
|
361
|
+
process.stderr.write(`[phren] vectorFallback fileRead: ${errorMessage(err)}\n`);
|
|
372
362
|
}
|
|
373
363
|
return { project: entryProject, filename, type, content, path: e.path };
|
|
374
364
|
});
|
package/mcp/dist/shared-sqljs.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
import { createRequire } from "module";
|
|
5
|
+
import { errorMessage } from "./utils.js";
|
|
5
6
|
const require = createRequire(import.meta.url);
|
|
6
7
|
/**
|
|
7
8
|
* Locate the sql.js-fts5 WASM binary by require.resolve with path-probe fallback.
|
|
@@ -14,8 +15,8 @@ function findWasmBinary() {
|
|
|
14
15
|
return fs.readFileSync(resolved);
|
|
15
16
|
}
|
|
16
17
|
catch (err) {
|
|
17
|
-
if ((process.env.PHREN_DEBUG
|
|
18
|
-
process.stderr.write(`[phren] findWasmBinary requireResolve: ${
|
|
18
|
+
if ((process.env.PHREN_DEBUG))
|
|
19
|
+
process.stderr.write(`[phren] findWasmBinary requireResolve: ${errorMessage(err)}\n`);
|
|
19
20
|
// fall through to path probing
|
|
20
21
|
}
|
|
21
22
|
const __filename = fileURLToPath(import.meta.url);
|
package/mcp/dist/shared.js
CHANGED
|
@@ -3,10 +3,9 @@ import * as path from "path";
|
|
|
3
3
|
import { debugLog, runtimeFile } from "./phren-paths.js";
|
|
4
4
|
import { errorMessage } from "./utils.js";
|
|
5
5
|
export { HOOK_TOOL_NAMES, hookConfigPath } from "./provider-adapters.js";
|
|
6
|
-
export { EXEC_TIMEOUT_MS, EXEC_TIMEOUT_QUICK_MS, PhrenError, phrenOk, phrenErr, forwardErr, parsePhrenErrorCode, isRecord, withDefaults, FINDING_TYPES, FINDING_TAGS, KNOWN_OBSERVATION_TAGS, DOC_TYPES, capCache, } from "./phren-core.js";
|
|
6
|
+
export { EXEC_TIMEOUT_MS, EXEC_TIMEOUT_QUICK_MS, PhrenError, phrenOk, phrenErr, forwardErr, parsePhrenErrorCode, isRecord, withDefaults, FINDING_TYPES, FINDING_TAGS, KNOWN_OBSERVATION_TAGS, DOC_TYPES, capCache, RESERVED_PROJECT_DIR_NAMES, } from "./phren-core.js";
|
|
7
7
|
export { ROOT_MANIFEST_FILENAME, homeDir, homePath, expandHomePath, defaultPhrenPath, rootManifestPath, readRootManifest, writeRootManifest, resolveInstallContext, findNearestPhrenPath, isProjectLocalMode, runtimeDir, tryUnlink, sessionsDir, runtimeFile, installPreferencesFile, runtimeHealthFile, shellStateFile, sessionMetricsFile, memoryScoresFile, memoryUsageLogFile, sessionMarker, debugLog, appendIndexEvent, resolveFindingsPath, findPhrenPath, ensurePhrenPath, findPhrenPathWithArg, normalizeProjectNameForCreate, findProjectNameCaseInsensitive, getProjectDirs, collectNativeMemoryFiles, computePhrenLiveStateToken, getPhrenPath, qualityMarkers, atomicWriteText, } from "./phren-paths.js";
|
|
8
8
|
export { PROACTIVITY_LEVELS, getProactivityLevel, getProactivityLevelForFindings, getProactivityLevelForTask, hasExplicitFindingSignal, hasExplicitTaskSignal, hasExecutionIntent, hasDiscoveryIntent, shouldAutoCaptureFindingsForLevel, shouldAutoCaptureTaskForLevel, } from "./proactivity.js";
|
|
9
|
-
const RESERVED_PROJECT_DIR_NAMES = new Set(["profiles", "templates", "global"]);
|
|
10
9
|
const MEMORY_SCOPE_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
11
10
|
export function normalizeMemoryScope(scope) {
|
|
12
11
|
if (typeof scope !== "string")
|
|
@@ -50,7 +49,7 @@ export function appendAuditLog(phrenPath, event, details) {
|
|
|
50
49
|
break;
|
|
51
50
|
}
|
|
52
51
|
catch (err) {
|
|
53
|
-
if ((process.env.PHREN_DEBUG
|
|
52
|
+
if ((process.env.PHREN_DEBUG))
|
|
54
53
|
process.stderr.write(`[phren] appendAuditLog lockWrite: ${errorMessage(err)}\n`);
|
|
55
54
|
try {
|
|
56
55
|
const stat = fs.statSync(lockPath);
|
|
@@ -68,7 +67,7 @@ export function appendAuditLog(phrenPath, event, details) {
|
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
69
|
catch (statErr) {
|
|
71
|
-
if ((process.env.PHREN_DEBUG
|
|
70
|
+
if ((process.env.PHREN_DEBUG))
|
|
72
71
|
process.stderr.write(`[phren] appendAuditLog staleStat: ${errorMessage(statErr)}\n`);
|
|
73
72
|
}
|
|
74
73
|
Atomics.wait(waiter, 0, 0, pollMs);
|
|
@@ -81,7 +80,7 @@ export function appendAuditLog(phrenPath, event, details) {
|
|
|
81
80
|
if (stat.size > 1_000_000) {
|
|
82
81
|
const content = fs.readFileSync(logPath, "utf8");
|
|
83
82
|
const lines = content.split("\n");
|
|
84
|
-
fs.writeFileSync(logPath, lines.slice(-500).join("\n"));
|
|
83
|
+
fs.writeFileSync(logPath, lines.slice(-500).join("\n") + "\n");
|
|
85
84
|
}
|
|
86
85
|
}
|
|
87
86
|
else {
|
|
@@ -97,7 +96,7 @@ export function appendAuditLog(phrenPath, event, details) {
|
|
|
97
96
|
fs.unlinkSync(lockPath);
|
|
98
97
|
}
|
|
99
98
|
catch (err) {
|
|
100
|
-
if ((process.env.PHREN_DEBUG
|
|
99
|
+
if ((process.env.PHREN_DEBUG))
|
|
101
100
|
process.stderr.write(`[phren] appendAuditLog unlock: ${errorMessage(err)}\n`);
|
|
102
101
|
}
|
|
103
102
|
}
|
package/mcp/dist/shell-entry.js
CHANGED
|
@@ -147,7 +147,7 @@ export async function startShell(phrenPath, profile) {
|
|
|
147
147
|
const shell = new PhrenShell(phrenPath, profile);
|
|
148
148
|
if (!process.stdin.isTTY) {
|
|
149
149
|
const { createInterface } = await import("readline");
|
|
150
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal:
|
|
150
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: process.stdin.isTTY ?? false });
|
|
151
151
|
const repaint = async () => { clearScreen(); process.stdout.write(await shell.render()); rl.setPrompt(`\n${style.boldCyan(":phren>")} `); rl.prompt(); };
|
|
152
152
|
const stopPoll = startLiveStatePoller({ phrenPath, shell, repaint });
|
|
153
153
|
await repaint();
|
package/mcp/dist/shell-input.js
CHANGED
|
@@ -379,7 +379,7 @@ export async function executePalette(host, input) {
|
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
381
|
catch (err) {
|
|
382
|
-
if ((process.env.PHREN_DEBUG
|
|
382
|
+
if ((process.env.PHREN_DEBUG))
|
|
383
383
|
process.stderr.write(`[phren] shell status gitStatus: ${errorMessage(err)}\n`);
|
|
384
384
|
}
|
|
385
385
|
const auditPathNew = runtimeFile(host.phrenPath, "audit.log");
|
|
@@ -740,6 +740,67 @@ function showCursorPosition(host) {
|
|
|
740
740
|
const short = label.length > 50 ? label.slice(0, 48) + "…" : label;
|
|
741
741
|
host.setMessage(` ${style.dim(`${cursor + 1} / ${count}`)}${short ? ` ${style.dimItalic(short)}` : ""}`);
|
|
742
742
|
}
|
|
743
|
+
// ── View shortcut keys (shared between handleInput text mode and handleNavigateKey) ─
|
|
744
|
+
/**
|
|
745
|
+
* Handle p/b/l/m/s/k/h shortcut keys that switch the active view.
|
|
746
|
+
* Returns true if the key was handled.
|
|
747
|
+
*/
|
|
748
|
+
export function applyViewShortcut(host, key) {
|
|
749
|
+
if (key === "p") {
|
|
750
|
+
host.setView("Projects");
|
|
751
|
+
host.setMessage(` ${TAB_ICONS.Projects} Projects`);
|
|
752
|
+
return true;
|
|
753
|
+
}
|
|
754
|
+
if (key === "b") {
|
|
755
|
+
if (!host.state.project) {
|
|
756
|
+
host.setMessage(style.dim(" Select a project first (↵)"));
|
|
757
|
+
return true;
|
|
758
|
+
}
|
|
759
|
+
host.setView("Tasks");
|
|
760
|
+
host.setMessage(` ${TAB_ICONS.Tasks} Tasks`);
|
|
761
|
+
return true;
|
|
762
|
+
}
|
|
763
|
+
if (key === "l") {
|
|
764
|
+
if (!host.state.project) {
|
|
765
|
+
host.setMessage(style.dim(" Select a project first (↵)"));
|
|
766
|
+
return true;
|
|
767
|
+
}
|
|
768
|
+
host.setView("Findings");
|
|
769
|
+
host.setMessage(` ${TAB_ICONS.Findings} Findings`);
|
|
770
|
+
return true;
|
|
771
|
+
}
|
|
772
|
+
if (key === "m") {
|
|
773
|
+
if (!host.state.project) {
|
|
774
|
+
host.setMessage(style.dim(" Select a project first (↵)"));
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
host.setView("Review Queue");
|
|
778
|
+
host.setMessage(` ${TAB_ICONS["Review Queue"]} Review Queue`);
|
|
779
|
+
return true;
|
|
780
|
+
}
|
|
781
|
+
if (key === "s") {
|
|
782
|
+
if (!host.state.project) {
|
|
783
|
+
host.setMessage(style.dim(" Select a project first (↵)"));
|
|
784
|
+
return true;
|
|
785
|
+
}
|
|
786
|
+
host.setView("Skills");
|
|
787
|
+
host.setMessage(` ${TAB_ICONS.Skills} Skills`);
|
|
788
|
+
return true;
|
|
789
|
+
}
|
|
790
|
+
if (key === "k") {
|
|
791
|
+
host.setView("Hooks");
|
|
792
|
+
host.setMessage(` ${TAB_ICONS.Hooks} Hooks`);
|
|
793
|
+
return true;
|
|
794
|
+
}
|
|
795
|
+
if (key === "h") {
|
|
796
|
+
host.prevHealthView = host.state.view === "Health" ? host.prevHealthView : host.state.view;
|
|
797
|
+
host.healthCache = undefined;
|
|
798
|
+
host.setView("Health");
|
|
799
|
+
host.setMessage(` ${TAB_ICONS.Health} Health ${style.dim("(esc to return)")}`);
|
|
800
|
+
return true;
|
|
801
|
+
}
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
743
804
|
// ── Navigate-mode key handler ─────────────────────────────────────────────────
|
|
744
805
|
export async function handleNavigateKey(host, key) {
|
|
745
806
|
if (key === "\x1b[A") {
|
|
@@ -836,59 +897,8 @@ export async function handleNavigateKey(host, key) {
|
|
|
836
897
|
}
|
|
837
898
|
return true;
|
|
838
899
|
}
|
|
839
|
-
if (key
|
|
840
|
-
host.setView("Projects");
|
|
841
|
-
host.setMessage(` ${TAB_ICONS.Projects} Projects`);
|
|
842
|
-
return true;
|
|
843
|
-
}
|
|
844
|
-
if (key === "b") {
|
|
845
|
-
if (!host.state.project) {
|
|
846
|
-
host.setMessage(style.dim(" Select a project first (↵)"));
|
|
847
|
-
return true;
|
|
848
|
-
}
|
|
849
|
-
host.setView("Tasks");
|
|
850
|
-
host.setMessage(` ${TAB_ICONS.Tasks} Tasks`);
|
|
900
|
+
if (applyViewShortcut(host, key))
|
|
851
901
|
return true;
|
|
852
|
-
}
|
|
853
|
-
if (key === "l") {
|
|
854
|
-
if (!host.state.project) {
|
|
855
|
-
host.setMessage(style.dim(" Select a project first (↵)"));
|
|
856
|
-
return true;
|
|
857
|
-
}
|
|
858
|
-
host.setView("Findings");
|
|
859
|
-
host.setMessage(` ${TAB_ICONS.Findings} Fragments`);
|
|
860
|
-
return true;
|
|
861
|
-
}
|
|
862
|
-
if (key === "m") {
|
|
863
|
-
if (!host.state.project) {
|
|
864
|
-
host.setMessage(style.dim(" Select a project first (↵)"));
|
|
865
|
-
return true;
|
|
866
|
-
}
|
|
867
|
-
host.setView("Review Queue");
|
|
868
|
-
host.setMessage(` ${TAB_ICONS["Review Queue"]} Review Queue`);
|
|
869
|
-
return true;
|
|
870
|
-
}
|
|
871
|
-
if (key === "s") {
|
|
872
|
-
if (!host.state.project) {
|
|
873
|
-
host.setMessage(style.dim(" Select a project first (↵)"));
|
|
874
|
-
return true;
|
|
875
|
-
}
|
|
876
|
-
host.setView("Skills");
|
|
877
|
-
host.setMessage(` ${TAB_ICONS.Skills} Skills`);
|
|
878
|
-
return true;
|
|
879
|
-
}
|
|
880
|
-
if (key === "k") {
|
|
881
|
-
host.setView("Hooks");
|
|
882
|
-
host.setMessage(` ${TAB_ICONS.Hooks} Hooks`);
|
|
883
|
-
return true;
|
|
884
|
-
}
|
|
885
|
-
if (key === "h") {
|
|
886
|
-
host.prevHealthView = host.state.view === "Health" ? host.prevHealthView : host.state.view;
|
|
887
|
-
host.healthCache = undefined;
|
|
888
|
-
host.setView("Health");
|
|
889
|
-
host.setMessage(` ${TAB_ICONS.Health} Health ${style.dim("(esc to return)")}`);
|
|
890
|
-
return true;
|
|
891
|
-
}
|
|
892
902
|
if (key === "i" && host.state.view === "Projects") {
|
|
893
903
|
const next = host.state.introMode === "always" ? "once-per-version" : host.state.introMode === "off" ? "always" : "off";
|
|
894
904
|
host.state.introMode = next;
|
|
@@ -7,7 +7,12 @@ import { EXEC_TIMEOUT_MS, } from "./shared.js";
|
|
|
7
7
|
export function resultMsg(r) {
|
|
8
8
|
if (!r.ok)
|
|
9
9
|
return r.error;
|
|
10
|
-
|
|
10
|
+
if (typeof r.data === "string")
|
|
11
|
+
return r.data;
|
|
12
|
+
if (r.data && typeof r.data === "object" && "message" in r.data && typeof r.data.message === "string") {
|
|
13
|
+
return r.data.message;
|
|
14
|
+
}
|
|
15
|
+
return JSON.stringify(r.data);
|
|
11
16
|
}
|
|
12
17
|
export function editDistance(a, b) {
|
|
13
18
|
const m = a.length;
|
package/mcp/dist/shell-render.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
// ── ANSI utilities ──────────────────────────────────────────────────────────
|
|
2
2
|
const ESC = "\x1b[";
|
|
3
3
|
export const RESET = `${ESC}0m`;
|
|
4
|
+
export const BOLD = `${ESC}1m`;
|
|
5
|
+
export const DIM = `${ESC}2m`;
|
|
6
|
+
export const GREEN = `${ESC}32m`;
|
|
7
|
+
export const YELLOW = `${ESC}33m`;
|
|
8
|
+
export const RED = `${ESC}31m`;
|
|
9
|
+
export const CYAN = `${ESC}36m`;
|
|
4
10
|
export const style = {
|
|
5
11
|
bold: (s) => `${ESC}1m${s}${RESET}`,
|
|
6
12
|
dim: (s) => `${ESC}2m${s}${RESET}`,
|
|
@@ -144,9 +150,7 @@ const PHREN_LOGO = [
|
|
|
144
150
|
"██║ ██║ ██║██║ ██║███████╗██║ ╚████║",
|
|
145
151
|
"╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝",
|
|
146
152
|
];
|
|
147
|
-
// Compact phren character for startup (uses PHREN_ART from phren-art.ts via import)
|
|
148
153
|
import { PHREN_ART as PHREN_STARTUP_ART } from "./phren-art.js";
|
|
149
|
-
const PHREN_STARTUP = PHREN_STARTUP_ART;
|
|
150
154
|
// ── Line-based viewport: edge-triggered scroll (stable, no jumpiness) ─────────
|
|
151
155
|
export function lineViewport(allLines, cursorFirstLine, cursorLastLine, height, prevStart) {
|
|
152
156
|
if (allLines.length === 0 || height <= 0)
|
|
@@ -181,7 +185,7 @@ export function shellHelpText() {
|
|
|
181
185
|
hdr("View-specific keys"),
|
|
182
186
|
` ${style.bold("Projects")} ${k("↵")} ${d("open project tasks")} ${k("i")} ${d("cycle intro mode")}`,
|
|
183
187
|
` ${style.bold("Tasks")} ${k("a")} ${d("add task")} ${k("d")} ${d("toggle active/queue")} ${k("↵")} ${d("mark complete")}`,
|
|
184
|
-
` ${style.bold("
|
|
188
|
+
` ${style.bold("Findings")} ${k("a")} ${d("tell phren")} ${k("d")} ${d("delete selected")}`,
|
|
185
189
|
` ${style.bold("Review Queue")} ${k("↵")} ${d("inspect selected item")} ${d("(read-only)")}`,
|
|
186
190
|
` ${style.bold("Skills")} ${k("t")} ${d("toggle enabled")} ${k("d")} ${d("remove")}`,
|
|
187
191
|
"",
|
|
@@ -221,7 +225,7 @@ export function shellStartupFrames(version) {
|
|
|
221
225
|
const versionBadge = badge(`v${version}`, style.boldBlue);
|
|
222
226
|
if (cols >= 72) {
|
|
223
227
|
// Side-by-side: phren character on left, logo text on right
|
|
224
|
-
const phrenLines =
|
|
228
|
+
const phrenLines = PHREN_STARTUP_ART;
|
|
225
229
|
const logoLines = PHREN_LOGO.map(line => gradient(line));
|
|
226
230
|
const infoLine = `${gradient("◆")} ${style.bold("phren")} ${versionBadge} ${tagline}`;
|
|
227
231
|
// Logo is 6 lines, pad to align vertically with character center
|
|
@@ -252,7 +256,7 @@ export function shellStartupFrames(version) {
|
|
|
252
256
|
];
|
|
253
257
|
}
|
|
254
258
|
// Narrow terminal: progressive text reveal with gradient
|
|
255
|
-
const stages = ["
|
|
259
|
+
const stages = ["p", "phr", "phren"];
|
|
256
260
|
const spinners = ["◜", "◠", "◝"];
|
|
257
261
|
return stages.map((stage, i) => [
|
|
258
262
|
"",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { phrenErr, PhrenError, phrenOk, shellStateFile } from "./shared.js";
|
|
4
|
-
import {
|
|
4
|
+
import { withFileLock as withFileLockRaw } from "./shared-governance.js";
|
|
5
5
|
import { errorMessage } from "./utils.js";
|
|
6
6
|
function withSafeLock(filePath, fn) {
|
|
7
7
|
try {
|
|
@@ -45,7 +45,7 @@ export function loadShellState(phrenPath) {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
catch (err) {
|
|
48
|
-
if ((process.env.PHREN_DEBUG
|
|
48
|
+
if ((process.env.PHREN_DEBUG))
|
|
49
49
|
process.stderr.write(`[phren] loadShellState parse: ${errorMessage(err)}\n`);
|
|
50
50
|
return fallback;
|
|
51
51
|
}
|
|
@@ -76,6 +76,3 @@ export function resetShellState(phrenPath) {
|
|
|
76
76
|
return phrenOk("Shell state reset.");
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
|
-
export function readRuntimeHealth(phrenPath) {
|
|
80
|
-
return getRuntimeHealth(phrenPath);
|
|
81
|
-
}
|
package/mcp/dist/shell-view.js
CHANGED
|
@@ -13,6 +13,7 @@ import { listMachines, listProfiles, } from "./data-access.js";
|
|
|
13
13
|
import { readInstallPreferences } from "./init-preferences.js";
|
|
14
14
|
import { isProjectHookEnabled, readProjectConfig } from "./project-config.js";
|
|
15
15
|
import { getScopedSkills } from "./skill-registry.js";
|
|
16
|
+
import { errorMessage } from "./utils.js";
|
|
16
17
|
// ── Tab bar ────────────────────────────────────────────────────────────────
|
|
17
18
|
function renderTabBar(state) {
|
|
18
19
|
const cols = renderWidth();
|
|
@@ -66,7 +67,7 @@ function renderBottomBar(state, navMode, inputCtx, inputBuf) {
|
|
|
66
67
|
Tasks: [`${k("a")} ${d("add")}`, `${k("↵")} ${d("mark done")}`, `${k("d")} ${d("toggle active")}`],
|
|
67
68
|
Findings: [`${k("a")} ${d("add")}`, `${k("d")} ${d("remove")}`],
|
|
68
69
|
"Review Queue": [`${k("↵")} ${d("inspect")}`],
|
|
69
|
-
Skills: [`${k("t")} ${d("toggle")}`, `${k("d")} ${d("remove")}`],
|
|
70
|
+
Skills: [`${k("a")} ${d("add")}`, `${k("t")} ${d("toggle")}`, `${k("d")} ${d("remove")}`],
|
|
70
71
|
Hooks: [`${k("a")} ${d("enable")}`, `${k("d")} ${d("disable")}`],
|
|
71
72
|
Health: [`${k("↑↓")} ${d("scroll")}`, `${k("esc")} ${d("back")}`],
|
|
72
73
|
};
|
|
@@ -135,9 +136,9 @@ function renderProjectsDashboard(ctx, entries, height) {
|
|
|
135
136
|
const findingsPreview = scoped
|
|
136
137
|
.filter((entry) => entry.findingCount > 0)
|
|
137
138
|
.slice(0, 3)
|
|
138
|
-
.map((entry) => `${style.bold(entry.name)} ${style.dim(`${entry.findingCount}
|
|
139
|
+
.map((entry) => `${style.bold(entry.name)} ${style.dim(`${entry.findingCount} findings`)}`);
|
|
139
140
|
const lines = [
|
|
140
|
-
` ${badge(ctx.profile || "default", style.boldBlue)} ${style.bold(String(scoped.length))} projects ${style.dim("·")} ${style.boldGreen(String(totals.active))} active ${style.dim("·")} ${style.boldYellow(String(totals.queue))} queued ${style.dim("·")} ${style.boldCyan(String(totals.findings))}
|
|
141
|
+
` ${badge(ctx.profile || "default", style.boldBlue)} ${style.bold(String(scoped.length))} projects ${style.dim("·")} ${style.boldGreen(String(totals.active))} active ${style.dim("·")} ${style.boldYellow(String(totals.queue))} queued ${style.dim("·")} ${style.boldCyan(String(totals.findings))} findings ${style.dim("·")} ${style.boldMagenta(String(totals.review))} review`,
|
|
141
142
|
ctx.state.project
|
|
142
143
|
? ` ${style.green("●")} active context ${style.boldCyan(ctx.state.project)} ${style.dim("· ↵ opens selected project tasks")}`
|
|
143
144
|
: ` ${style.dim("No project selected yet")} ${style.dim("· ↵ sets context and opens tasks")}`,
|
|
@@ -248,8 +249,8 @@ function parseSubsections(taskPath, project, cache) {
|
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
catch (err) {
|
|
251
|
-
if ((process.env.PHREN_DEBUG
|
|
252
|
-
process.stderr.write(`[phren] buildSubsectionMap: ${
|
|
252
|
+
if ((process.env.PHREN_DEBUG))
|
|
253
|
+
process.stderr.write(`[phren] buildSubsectionMap: ${errorMessage(err)}\n`);
|
|
253
254
|
}
|
|
254
255
|
const newCache = { project, map };
|
|
255
256
|
return { map, cache: newCache };
|
|
@@ -516,7 +517,7 @@ function renderSkillsView(ctx, cursor, height) {
|
|
|
516
517
|
}
|
|
517
518
|
const LIFECYCLE_HOOKS = [
|
|
518
519
|
{ event: "UserPromptSubmit", description: "inject context before each prompt" },
|
|
519
|
-
{ event: "Stop", description: "phren saves
|
|
520
|
+
{ event: "Stop", description: "phren saves findings after each response" },
|
|
520
521
|
{ event: "SessionStart", description: "git pull at session start" },
|
|
521
522
|
];
|
|
522
523
|
export function getHookEntries(phrenPath, project) {
|
package/mcp/dist/shell.js
CHANGED
|
@@ -2,11 +2,11 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { addTask, addFinding, loadShellState, saveShellState, } from "./data-access.js";
|
|
4
4
|
import { style } from "./shell-render.js";
|
|
5
|
-
import { MAX_UNDO_STACK,
|
|
5
|
+
import { MAX_UNDO_STACK, } from "./shell-types.js";
|
|
6
6
|
import { resultMsg, defaultRunHooks, defaultRunUpdate, defaultRunRelink, } from "./shell-palette.js";
|
|
7
7
|
import { runDoctor } from "./link.js";
|
|
8
8
|
import { renderShell, } from "./shell-view.js";
|
|
9
|
-
import { executePalette, completeInput as completeInputFn, getListItems, handleNavigateKey, } from "./shell-input.js";
|
|
9
|
+
import { executePalette, completeInput as completeInputFn, getListItems, handleNavigateKey, applyViewShortcut, } from "./shell-input.js";
|
|
10
10
|
import { errorMessage } from "./utils.js";
|
|
11
11
|
// ── Shell class ──────────────────────────────────────────────────────────────
|
|
12
12
|
export class PhrenShell {
|
|
@@ -16,7 +16,7 @@ export class PhrenShell {
|
|
|
16
16
|
state;
|
|
17
17
|
message = ` ${style.boldCyan("←→")} ${style.dim("tabs")} ${style.boldCyan("↑↓")} ${style.dim("move")} ${style.boldCyan("↵")} ${style.dim("activate")} ${style.boldCyan("?")} ${style.dim("help")}`;
|
|
18
18
|
healthCache;
|
|
19
|
-
prevHealthView;
|
|
19
|
+
prevHealthView = undefined;
|
|
20
20
|
showHelp = false;
|
|
21
21
|
pendingConfirm;
|
|
22
22
|
undoStack = [];
|
|
@@ -72,7 +72,7 @@ export class PhrenShell {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
catch (err) {
|
|
75
|
-
if ((process.env.PHREN_DEBUG
|
|
75
|
+
if ((process.env.PHREN_DEBUG))
|
|
76
76
|
process.stderr.write(`[phren] shell pushUndo: ${errorMessage(err)}\n`);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
@@ -277,58 +277,8 @@ export class PhrenShell {
|
|
|
277
277
|
return true;
|
|
278
278
|
if (["q", "quit", ":q", ":quit", ":exit"].includes(input.toLowerCase()))
|
|
279
279
|
return false;
|
|
280
|
-
if (input
|
|
281
|
-
this.setView("Projects");
|
|
282
|
-
this.setMessage(` ${TAB_ICONS.Projects} Projects`);
|
|
280
|
+
if (applyViewShortcut(this.asNavigationHost(), input))
|
|
283
281
|
return true;
|
|
284
|
-
}
|
|
285
|
-
if (input === "b") {
|
|
286
|
-
if (!this.state.project) {
|
|
287
|
-
this.setMessage(style.dim(" Select a project first (↵)"));
|
|
288
|
-
return true;
|
|
289
|
-
}
|
|
290
|
-
this.setView("Tasks");
|
|
291
|
-
this.setMessage(` ${TAB_ICONS.Tasks} Tasks`);
|
|
292
|
-
return true;
|
|
293
|
-
}
|
|
294
|
-
if (input === "l") {
|
|
295
|
-
if (!this.state.project) {
|
|
296
|
-
this.setMessage(style.dim(" Select a project first (↵)"));
|
|
297
|
-
return true;
|
|
298
|
-
}
|
|
299
|
-
this.setView("Findings");
|
|
300
|
-
this.setMessage(` ${TAB_ICONS.Findings} Fragments`);
|
|
301
|
-
return true;
|
|
302
|
-
}
|
|
303
|
-
if (input === "m") {
|
|
304
|
-
if (!this.state.project) {
|
|
305
|
-
this.setMessage(style.dim(" Select a project first (↵)"));
|
|
306
|
-
return true;
|
|
307
|
-
}
|
|
308
|
-
this.setView("Review Queue");
|
|
309
|
-
this.setMessage(` ${TAB_ICONS["Review Queue"]} Review Queue`);
|
|
310
|
-
return true;
|
|
311
|
-
}
|
|
312
|
-
if (input === "s") {
|
|
313
|
-
if (!this.state.project) {
|
|
314
|
-
this.setMessage(style.dim(" Select a project first (↵)"));
|
|
315
|
-
return true;
|
|
316
|
-
}
|
|
317
|
-
this.setView("Skills");
|
|
318
|
-
this.setMessage(` ${TAB_ICONS.Skills} Skills`);
|
|
319
|
-
return true;
|
|
320
|
-
}
|
|
321
|
-
if (input === "k") {
|
|
322
|
-
this.setView("Hooks");
|
|
323
|
-
this.setMessage(` ${TAB_ICONS.Hooks} Hooks`);
|
|
324
|
-
return true;
|
|
325
|
-
}
|
|
326
|
-
if (input === "h") {
|
|
327
|
-
this.healthCache = undefined;
|
|
328
|
-
this.setView("Health");
|
|
329
|
-
this.setMessage(` ${TAB_ICONS.Health} Health`);
|
|
330
|
-
return true;
|
|
331
|
-
}
|
|
332
282
|
if (input.startsWith("/")) {
|
|
333
283
|
this.setFilter(input.slice(1));
|
|
334
284
|
return true;
|