fuli-context 0.5.0 → 0.7.2
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.en.md +2 -333
- package/README.md +370 -202
- package/README.zh-CN.md +448 -0
- package/compose.graphiti.yml +6 -6
- package/dist/web/assets/AboutPage-DXh7Fwxt.css +1 -0
- package/dist/web/assets/AboutPage-DfnZo7Lo.js +1 -0
- package/dist/web/assets/ConnectionsPage-BloaSKmt.js +1 -0
- package/dist/web/assets/ConnectionsPage-CBHAdZXT.css +1 -0
- package/dist/web/assets/KnowledgeInspector-Bjhdu2mf.css +1 -0
- package/dist/web/assets/KnowledgeInspector-QvNpv0_k.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-Cx1w1jb2.css +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-DKq1jVD5.js +1 -0
- package/dist/web/assets/KnowledgePage-BWL0jT01.js +1 -0
- package/dist/web/assets/KnowledgeWorkspace-Czq6aZ7b.js +4 -0
- package/dist/web/assets/OverviewPage-5sJoO1Gh.js +1 -0
- package/dist/web/assets/{PersonalProfilePage-S8ZcG2LU.css → PersonalProfilePage-CdmEzyvZ.css} +1 -1
- package/dist/web/assets/PersonalProfilePage-tVChM9ik.js +1 -0
- package/dist/web/assets/PersonalProjectsPage-BPFrup4V.js +1 -0
- package/dist/web/assets/PublicProjectsPage-BPJSeREN.js +1 -0
- package/dist/web/assets/ReviewPage-B7EN817i.js +1 -0
- package/dist/web/assets/SearchableSelect-ClLN0rIb.js +1 -0
- package/dist/web/assets/SettingsPage-C6ouU0sR.css +1 -0
- package/dist/web/assets/SettingsPage-Chd_B3eH.js +1 -0
- package/dist/web/assets/_plugin-vue_export-helper-BDNMzG2s.js +1 -0
- package/dist/web/assets/console-CbDsKrsO.js +1 -0
- package/dist/web/assets/fuli-logo-Bu9WBuQe.js +1 -0
- package/dist/web/assets/fuli-logo-ER1wQ8Kk.png +0 -0
- package/dist/web/assets/i18n-BQcns1PH.js +5 -0
- package/dist/web/assets/identity-B8K4VYg6.js +1 -0
- package/dist/web/assets/index-CB8Xmsc2.js +2 -0
- package/dist/web/assets/index-CZeuPh-U.css +1 -0
- package/dist/web/assets/model-eF52GgPF.js +1 -0
- package/dist/web/assets/paths-hwz-Ac1g.js +1 -0
- package/dist/web/assets/useMinimumLoadingDisplay-BpCyBeko.css +1 -0
- package/dist/web/assets/useMinimumLoadingDisplay-wCgGDmHg.js +1 -0
- package/dist/web/assets/vue-router-BsEp_XwI.js +1 -0
- package/dist/web/index.html +9 -6
- package/docs/external-knowledge-architecture.md +279 -0
- package/examples/external-knowledge/markdown-folder.mjs +235 -0
- package/graph-provider/fuli_graph/app.py +57 -1
- package/graph-provider/fuli_graph/graph_models.py +3 -0
- package/graph-provider/fuli_graph/graph_query.py +62 -16
- package/graph-provider/fuli_graph/knowledge_review.py +612 -0
- package/graph-provider/fuli_graph/knowledge_review_models.py +127 -0
- package/graph-provider/fuli_graph/knowledge_search.py +51 -4
- package/graph-provider/fuli_graph/models.py +19 -0
- package/graph-provider/fuli_graph/runtime.py +15 -0
- package/graph-provider/fuli_graph/store_knowledge.py +2 -1
- package/graph-provider/pyproject.toml +1 -1
- package/npm-shrinkwrap.json +5 -407
- package/package.json +7 -5
- package/skills/capturing-session-knowledge/SKILL.md +24 -4
- package/skills/flreview/SKILL.md +73 -0
- package/skills/flreview/agents/openai.yaml +6 -0
- package/src/agent-tools/graph-definitions.js +79 -4
- package/src/agent-tools/graph-handlers.js +12 -0
- package/src/cli/command-registry.js +3 -3
- package/src/cli/local-runtime-command.js +51 -28
- package/src/cli/local-runtime-options.js +14 -8
- package/src/cli/setup-agent-selection.js +12 -12
- package/src/cli/setup-command.js +41 -18
- package/src/cli/shell-argument.js +10 -0
- package/src/cli/uninstall-command.js +17 -15
- package/src/cli/update-command.js +38 -30
- package/src/external-knowledge/catalog.js +61 -0
- package/src/external-knowledge/conflict-policy.js +58 -0
- package/src/external-knowledge/connected-search.js +166 -0
- package/src/external-knowledge/connectors/custom.js +133 -0
- package/src/external-knowledge/connectors/feishu.js +461 -0
- package/src/external-knowledge/connectors/mcp.js +438 -0
- package/src/external-knowledge/connectors/notion.js +307 -0
- package/src/external-knowledge/connectors/retrieval-api.js +222 -0
- package/src/external-knowledge/document-mapping.js +151 -0
- package/src/external-knowledge/graph-projection.js +17 -0
- package/src/external-knowledge/index.js +8 -0
- package/src/external-knowledge/registry.js +91 -0
- package/src/external-knowledge/runtime.js +68 -0
- package/src/external-knowledge/safe-diagnostic.js +14 -0
- package/src/external-knowledge/service.js +811 -0
- package/src/graphiti/agent-knowledge-workflows.js +7 -2
- package/src/graphiti/federated-application.js +66 -41
- package/src/graphiti/knowledge-item-role.js +21 -0
- package/src/graphiti/knowledge-review.js +43 -0
- package/src/graphiti/project-profile-mapping.js +32 -0
- package/src/graphiti/provider-client.js +22 -1
- package/src/graphiti/source-marker.js +54 -3
- package/src/graphiti/task-knowledge-recall.js +250 -0
- package/src/http/api-router.js +23 -108
- package/src/http/external-knowledge-api-router.js +104 -0
- package/src/http/graph-api-router.js +12 -1
- package/src/http/request-policy.js +1 -1
- package/src/local-runtime/lifecycle.js +26 -15
- package/src/mcp/create-mcp-server.js +1 -0
- package/src/mcp/instructions.js +7 -7
- package/src/mcp/tool-annotations.js +6 -0
- package/src/mcp/tool-result.js +1 -0
- package/src/server/application-lifecycle.js +2 -11
- package/src/server/blocked-ports.js +11 -0
- package/src/server.js +36 -17
- package/src/setup/agent-installation-status.js +2 -1
- package/src/setup/agents.js +2 -1
- package/src/setup/claude-code-config.js +2 -1
- package/src/setup/codex-bootstrap.js +12 -7
- package/src/setup/container-runtime.js +25 -21
- package/src/setup/graph-runtime.js +148 -56
- package/src/setup/node-runtime.js +3 -3
- package/src/setup/options.js +2 -4
- package/src/setup/paths.js +18 -3
- package/src/setup/review-skill-adapters.js +49 -0
- package/src/setup/setup.js +26 -2
- package/src/setup/uninstall.js +4 -2
- package/src/system/resource-monitor.js +317 -0
- package/src/system/runtime-settings.js +131 -0
- package/src/system/system-service.js +119 -0
- package/dist/web/assets/ConnectionsPage-CCKWm-6p.js +0 -1
- package/dist/web/assets/KnowledgeInspector-BZkB2wJn.js +0 -1
- package/dist/web/assets/KnowledgeInspector-CrGYxEob.css +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-CZAF_OlN.css +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-DlvcLwlL.js +0 -1
- package/dist/web/assets/KnowledgePage-CNQXd15y.js +0 -1
- package/dist/web/assets/KnowledgeWorkspace-CXPE4GCD.js +0 -4
- package/dist/web/assets/OverviewPage-ul7g7HXh.js +0 -1
- package/dist/web/assets/PersonalProfilePage-CC40Pw2L.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-CPIEcShD.js +0 -1
- package/dist/web/assets/PublicProjectsPage-C700sR7z.js +0 -1
- package/dist/web/assets/ReviewPage-7MDDUDgA.js +0 -1
- package/dist/web/assets/console-B_Ena2DS.js +0 -5
- package/dist/web/assets/fuli-logo-DGuyqchQ.png +0 -0
- package/dist/web/assets/identity-CjBwYSMX.js +0 -1
- package/dist/web/assets/index-CpLDx3of.css +0 -1
- package/dist/web/assets/index-DAzeFJSc.js +0 -2
- package/dist/web/assets/model-C-5RVIJ2.js +0 -1
- package/dist/web/assets/paths-BkULqmGI.js +0 -1
- package/dist/web/assets/vue-router-DnD3hUm7.js +0 -1
- package/src/agent/candidate-read-service.js +0 -53
- package/src/agent/context-pack-service.js +0 -83
- package/src/agent/public-read-policy.js +0 -39
- package/src/agent/public-read-service.js +0 -69
- package/src/agent/read-projection.js +0 -107
- package/src/agent/search-service.js +0 -61
- package/src/agent/space-boundary.js +0 -33
- package/src/app/bootstrap.js +0 -8
- package/src/app/create-application.js +0 -143
- package/src/app/state-service.js +0 -38
- package/src/candidates.js +0 -78
- package/src/classifier.js +0 -63
- package/src/cli/command-arguments.js +0 -14
- package/src/context-pack.js +0 -206
- package/src/extractor.js +0 -77
- package/src/fact-history.js +0 -39
- package/src/facts/fact-service.js +0 -89
- package/src/http/bounded-json-array.js +0 -19
- package/src/http/lens-route.js +0 -50
- package/src/ingestion.js +0 -161
- package/src/lens/lens-correction-service.js +0 -81
- package/src/lens/lens-fact-writer.js +0 -85
- package/src/lens/lens-input.js +0 -62
- package/src/lens/lens-query-projection.js +0 -87
- package/src/lens/lens-query-ranking.js +0 -62
- package/src/lens/lens-query.js +0 -143
- package/src/lens/lens-resource-projection.js +0 -93
- package/src/lens/lens-resource-selection.js +0 -45
- package/src/lens/lens-resource-service.js +0 -103
- package/src/lens/lens-search-query.js +0 -122
- package/src/lens/lens-service.js +0 -183
- package/src/models.js +0 -44
- package/src/observer.js +0 -83
- package/src/project-rules.js +0 -33
- package/src/publication/outbox-service.js +0 -46
- package/src/publication/publication-envelope-schema.js +0 -75
- package/src/publication/publication-envelope.js +0 -37
- package/src/publication/publication-policy.js +0 -82
- package/src/publication/publication-service.js +0 -48
- package/src/router.js +0 -58
- package/src/runtime-options.js +0 -138
- package/src/security/episode-visibility.js +0 -14
- package/src/security/fact-visibility.js +0 -15
- package/src/security/source-metadata-policy.js +0 -8
- package/src/storage/episode-evidence-preview.js +0 -100
- package/src/storage/fact-search-order.js +0 -18
- package/src/storage/file/episode-evidence.js +0 -37
- package/src/storage/file/episode-index.js +0 -59
- package/src/storage/file/fact-search.js +0 -60
- package/src/storage/file-store.js +0 -437
- package/src/storage/migrate.js +0 -128
- package/src/storage/migrations/001-initial.sql +0 -83
- package/src/storage/migrations/002-storage-integrity.sql +0 -113
- package/src/storage/record-validation.js +0 -46
- package/src/storage/snapshot-defaults.js +0 -54
- package/src/storage/sqlite/candidates-repository.js +0 -84
- package/src/storage/sqlite/episode-evidence-repository.js +0 -154
- package/src/storage/sqlite/episode-privacy-sql.js +0 -33
- package/src/storage/sqlite/episodes-repository.js +0 -52
- package/src/storage/sqlite/fact-search-repository.js +0 -162
- package/src/storage/sqlite/facts-repository.js +0 -146
- package/src/storage/sqlite/imports-repository.js +0 -39
- package/src/storage/sqlite/mapper.js +0 -84
- package/src/storage/sqlite/open-database.js +0 -15
- package/src/storage/sqlite/outbox-repository.js +0 -103
- package/src/storage/sqlite/snapshot-repository.js +0 -69
- package/src/storage/sqlite/spaces-repository.js +0 -52
- package/src/storage/sqlite/subscriptions-repository.js +0 -54
- package/src/storage/sqlite-store.js +0 -159
- package/src/storage/stable-json.js +0 -24
- package/src/storage/store-port.js +0 -20
- package/src/store.js +0 -2
|
@@ -12,14 +12,17 @@ Accumulate reusable knowledge without interrupting the user's normal work. Keep
|
|
|
12
12
|
At the start of every user task, first use the lifecycle context supplied by the host when one
|
|
13
13
|
exists. A Claude Code `UserPromptSubmit` hook calls `begin_task_context`, which already loads
|
|
14
14
|
collaboration preferences and resolves the exact local personal project from the current working
|
|
15
|
-
directory.
|
|
15
|
+
directory. It also uses the submitted prompt transiently for bounded automatic project-memory
|
|
16
|
+
recall and never stores or returns that prompt. Apply the returned `task_knowledge_recall` and
|
|
17
|
+
preferences, then retain the opaque task token for the final checkpoint;
|
|
16
18
|
do not redundantly call `get_collaboration_preferences`.
|
|
17
19
|
|
|
18
20
|
When the host has not supplied lifecycle context, call exactly
|
|
19
21
|
`get_collaboration_preferences` before any other tool or answer. Pass `projectPath` as the current
|
|
20
|
-
working directory. This is the prompt-only fallback
|
|
21
|
-
hook. Fuli uses
|
|
22
|
-
exact local personal project itself. Do not infer
|
|
22
|
+
working directory and `taskPrompt` as the current user request. This is the prompt-only fallback
|
|
23
|
+
for Agents without an equivalent lifecycle hook. Fuli uses both only for this local MCP call,
|
|
24
|
+
never stores or returns them, and resolves the exact local personal project itself. Do not infer
|
|
25
|
+
or guess `personalProjectId` in the Agent.
|
|
23
26
|
|
|
24
27
|
Apply only `effective_preferences`. Personal-global preferences apply in every user task;
|
|
25
28
|
project-scoped preferences layer on only for the exact selected project. Do not apply items
|
|
@@ -42,6 +45,9 @@ current working directory. It resolves the exact active project without requirin
|
|
|
42
45
|
copy an ID, searches that child project first, and then includes only inheritable knowledge from
|
|
43
46
|
authorized parents or shared-source projects. Use `search_knowledge_graph` for personal-global
|
|
44
47
|
context, explicitly named extra projects, subscribed public projects, or advanced scoped queries.
|
|
48
|
+
Use `search_connected_knowledge` for a project-bound third-party source or explicitly selected
|
|
49
|
+
public project. Keep its source sets separate and obey the project's conflict policy;
|
|
50
|
+
`agent_decide` affects only the current response and never mutates or confirms a source.
|
|
45
51
|
|
|
46
52
|
Search when prior durable context can materially improve the task.
|
|
47
53
|
Before saying that a stable fact is unknown or asking the user to provide it again, search
|
|
@@ -50,6 +56,15 @@ requirements, terminology, architecture, prior decisions, runbooks, rationale, a
|
|
|
50
56
|
personal preferences. Do not search for a fully self-contained task or use Fuli as proof of live
|
|
51
57
|
external state.
|
|
52
58
|
|
|
59
|
+
First inspect `task_knowledge_recall` returned by the lifecycle or preference tool. Its automatic
|
|
60
|
+
query plan is deliberately bounded and based on implementation trigger categories, not on a
|
|
61
|
+
claim that every returned candidate supports the task. If it misses or its candidates are
|
|
62
|
+
irrelevant while durable context may still matter, call `search_current_project_knowledge`
|
|
63
|
+
yourself. Derive one to four focused queries from the action, artifact, target system, distinctive
|
|
64
|
+
identifier, and likely runbook category. Never pass the full conversational request as the sole
|
|
65
|
+
query. For example, a version-release request should search release/publish and push/commit
|
|
66
|
+
runbook concepts separately instead of searching the entire sentence.
|
|
67
|
+
|
|
53
68
|
Pending knowledge is eligible for on-demand retrieval and must remain visibly marked.
|
|
54
69
|
`agent_confirmed` knowledge is usable but ranks below user or authoritative-source
|
|
55
70
|
confirmation.
|
|
@@ -156,6 +171,11 @@ Capture confirmed, stable information:
|
|
|
156
171
|
- Personal: the user's enduring preferences, boundaries, habits, and working methods. Project-specific personal rules must explicitly link to that project instead of becoming global defaults.
|
|
157
172
|
- Project: PRD facts, requirements, terminology, decisions, constraints, architecture, routes, APIs, metrics, and document-derived knowledge.
|
|
158
173
|
|
|
174
|
+
For a runbook, decision, route, or other item likely to be recalled from shorthand, add a bounded
|
|
175
|
+
`attributes.searchTerms` string array containing only neutral retrieval phrases supported by the
|
|
176
|
+
item and observed task vocabulary. Search terms are retrieval metadata, not new factual claims;
|
|
177
|
+
do not add speculative synonyms, credentials, or unrelated product names.
|
|
178
|
+
|
|
159
179
|
Do not capture credentials, tokens, cookies, private keys, raw transcripts, temporary logs, command output, speculative conclusions, or disposable implementation details. Treat a correction as a replacement, not an additional conflicting fact.
|
|
160
180
|
|
|
161
181
|
For a confirmed project decision, prefer `record_decision_trace` over a flat note. Preserve the
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flreview
|
|
3
|
+
description: Use when the user explicitly enters /flreview or explicitly asks to run the Fuli personal knowledge review command.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fuli Knowledge Review
|
|
7
|
+
|
|
8
|
+
Review personal preferences and local personal-project knowledge with the user as the authority. Never silently rewrite, invalidate, promote, or globalize knowledge.
|
|
9
|
+
|
|
10
|
+
## Select Scope
|
|
11
|
+
|
|
12
|
+
For every `/flreview`, first offer exactly these top-level choices:
|
|
13
|
+
|
|
14
|
+
1. **全部** — review personal-global preferences plus every local personal project. Each project includes its project knowledge and project-scoped personal preferences.
|
|
15
|
+
2. **个人偏好** — then choose **全局偏好** or the preferences for **某一个本地个人项目**.
|
|
16
|
+
3. **个人项目** — then choose **一个本地个人项目** or **全部本地个人项目**. Each selected project includes both project knowledge and project-scoped personal preferences.
|
|
17
|
+
|
|
18
|
+
Use `list_personal_projects` only when a branch needs a project choice. Never include subscribed or public projects. Do not infer the current directory as the selected project and do not invent another command variant.
|
|
19
|
+
|
|
20
|
+
## Calibrate Depth
|
|
21
|
+
|
|
22
|
+
After scope selection, first ask how much patience the user has for this review.
|
|
23
|
+
|
|
24
|
+
交互硬规则:先询问用户有没有耐心;如果用户表示完全没耐心,不要再询问心情、时间或 token,只问少量最高优先级的关键问题。
|
|
25
|
+
|
|
26
|
+
- If the user says they are **完全没耐心**, immediately take the short path. Do not ask about mood, available time, or token budget. Ask only a small number of the highest-priority critical questions, one at a time, and make stopping easy.
|
|
27
|
+
- Otherwise ask, concisely, about current mood, available time, and whether token budget is comfortable. Use the answers plus explicit requests such as “少问一点”, “多问一点”, or “直接开始” to choose page size and explanation depth.
|
|
28
|
+
- Do not impose a fixed total-question cap. A tool page limit controls only one retrieval batch, not the length of the review.
|
|
29
|
+
|
|
30
|
+
## Start Or Resume
|
|
31
|
+
|
|
32
|
+
Call `start_knowledge_review` with the exact selected scope and project ID when required. Reuse a returned active or paused run. Then call `list_knowledge_review_candidates` in bounded pages.
|
|
33
|
+
|
|
34
|
+
If there is no previous completed review for that exact scope, review all historical in-scope knowledge. Otherwise present candidates in this order:
|
|
35
|
+
|
|
36
|
+
没有上次完成记录时扫描全部历史。
|
|
37
|
+
|
|
38
|
+
1. Knowledge created or changed since the last completed review.
|
|
39
|
+
2. Current or older conflicts, negative evidence, or items marked `requires_attention`.
|
|
40
|
+
3. Low-weight knowledge, using the Provider's returned utility/confidence reasons.
|
|
41
|
+
4. The same durable pattern repeated across multiple sessions that may deserve a personal-global attribute.
|
|
42
|
+
|
|
43
|
+
Treat ranking thresholds as Provider policy, not user requirements. Do not calculate a second conflicting score in the Agent.
|
|
44
|
+
|
|
45
|
+
## Review One Candidate
|
|
46
|
+
|
|
47
|
+
Show the content, scope, confirmation state, relevant evidence summary, weights, and why the item was selected. Ask for one outcome:
|
|
48
|
+
|
|
49
|
+
- **确认保留** — keep it; if it is pending and the user explicitly confirms the content and classification, use the normal Fuli confirmation operation first.
|
|
50
|
+
- **修改** — obtain the exact replacement text, apply `revise_personal_knowledge`, then record progress.
|
|
51
|
+
- **失效** — reconfirm the exact target, apply `revise_personal_knowledge` with `invalidate`, then record progress.
|
|
52
|
+
- **跳过** — do not ask again in this run; it may return in a later review.
|
|
53
|
+
- **稍后处理** — do not ask again in this run; intentionally return it in the next review.
|
|
54
|
+
|
|
55
|
+
After a successful mutation—or immediately for keep, skip, or later—call `record_knowledge_review_progress`. Never record a mutation outcome before its write succeeds.
|
|
56
|
+
|
|
57
|
+
For repeated cross-session patterns, ask whether the user wants a personal-global attribute, a project-scoped preference, or no promotion. Never auto-promote. Use `set_personal_preference_scope` only after explicit confirmation and only for an actual preference item.
|
|
58
|
+
|
|
59
|
+
## Pause Or Complete
|
|
60
|
+
|
|
61
|
+
If the user stops before the selected queue is exhausted, call `finish_knowledge_review` with `paused`. Resume that run next time. Only call it with `completed` after the user explicitly finishes the selected scope or no candidates remain.
|
|
62
|
+
|
|
63
|
+
Only a completed run advances the last-completed review time. Paused runs do not. A “稍后处理” item returns in the next run; a “跳过” item stays out only for the current run.
|
|
64
|
+
|
|
65
|
+
只有完成回顾才推进上次回顾时间;暂停不能推进。
|
|
66
|
+
|
|
67
|
+
## Guardrails
|
|
68
|
+
|
|
69
|
+
- Keep the user authoritative. Explain conflicts; do not resolve them silently.
|
|
70
|
+
- Preserve original evidence and revision history.
|
|
71
|
+
- Never include raw transcripts, credentials, secrets, temporary logs, or unsupported inference.
|
|
72
|
+
- Do not turn repeated project facts into global personal attributes without explicit approval.
|
|
73
|
+
- If a required Fuli review tool is unavailable, state that the installed integration needs updating; do not simulate persistence in prose.
|
|
@@ -21,6 +21,13 @@ const sourceUri = {
|
|
|
21
21
|
pattern: '^[Hh][Tt][Tt][Pp][Ss]?://\\S+$'
|
|
22
22
|
};
|
|
23
23
|
const attributes = { type: 'object', additionalProperties: true };
|
|
24
|
+
const entityAttributes = {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
searchTerms: arraySchema(boundedString(256), { minItems: 1, maxItems: 32 })
|
|
28
|
+
},
|
|
29
|
+
additionalProperties: true
|
|
30
|
+
};
|
|
24
31
|
const epistemicQuadrant = enumSchema([
|
|
25
32
|
'known_known', 'known_unknown', 'unknown_known', 'unknown_unknown'
|
|
26
33
|
]);
|
|
@@ -48,6 +55,9 @@ const confirmationBasis = objectSchema({
|
|
|
48
55
|
agentPolicyVersion: nullableStringSchema()
|
|
49
56
|
}, ['existenceReason', 'quadrantReason', 'proposedBy']);
|
|
50
57
|
const knowledgeItemKind = enumSchema(['entity', 'relationship']);
|
|
58
|
+
const knowledgeReviewScope = enumSchema([
|
|
59
|
+
'all', 'preferences_global', 'preferences_project', 'projects_all', 'project'
|
|
60
|
+
]);
|
|
51
61
|
const humanChangeStatus = enumSchema([
|
|
52
62
|
'all', 'human_changed', 'unseen', 'viewed', 'reviewed'
|
|
53
63
|
]);
|
|
@@ -84,7 +94,7 @@ const entity = objectSchema({
|
|
|
84
94
|
type: { ...boundedString(64), pattern: '^[A-Za-z][A-Za-z0-9_]*$' },
|
|
85
95
|
summary: boundedString(4096),
|
|
86
96
|
...epistemicFields,
|
|
87
|
-
attributes
|
|
97
|
+
attributes: entityAttributes
|
|
88
98
|
}, [
|
|
89
99
|
'key', 'name', 'type', 'originQuadrant',
|
|
90
100
|
'confirmationStatus', 'confirmationBasis'
|
|
@@ -202,10 +212,11 @@ export const GRAPH_TOOL_DEFINITIONS = [
|
|
|
202
212
|
{
|
|
203
213
|
name: 'begin_task_context',
|
|
204
214
|
title: 'LIFECYCLE · Begin a Fuli-aware task',
|
|
205
|
-
description: 'Lifecycle entry used by supported Agent hooks before the model processes a user prompt. It resolves the exact local project from projectPath, loads effective collaboration preferences, and
|
|
215
|
+
description: 'Lifecycle entry used by supported Agent hooks before the model processes a user prompt. It resolves the exact local project from projectPath, loads effective collaboration preferences, and uses taskPrompt transiently for bounded automatic recall when the request signals a stable prior fact, runbook, URL, decision, release, deployment, or authentication method. It never stores or returns projectPath or taskPrompt. Inspect task_knowledge_recall before asking the user to repeat stable context. Returns an opaque taskContextToken. Claude Code setup installs this as a deterministic UserPromptSubmit hook; manual Agent calls are a fallback.',
|
|
206
216
|
inputSchema: objectSchema({
|
|
207
217
|
sessionId: id,
|
|
208
|
-
projectPath: boundedString(4096)
|
|
218
|
+
projectPath: boundedString(4096),
|
|
219
|
+
taskPrompt: boundedString(8192)
|
|
209
220
|
}, ['sessionId', 'projectPath'])
|
|
210
221
|
},
|
|
211
222
|
{
|
|
@@ -228,9 +239,10 @@ export const GRAPH_TOOL_DEFINITIONS = [
|
|
|
228
239
|
{
|
|
229
240
|
name: 'get_collaboration_preferences',
|
|
230
241
|
title: 'READ FIRST · Load task collaboration preferences',
|
|
231
|
-
description: 'Fallback for Agents or tasks without begin_task_context hook context. When no hook context was supplied, call this exact tool name at the start of every user task before any other tool or answer; never substitute a project action tool. Do not call it redundantly when the entry hook already supplied preferences. Pass projectPath as the current working directory
|
|
242
|
+
description: 'Fallback for Agents or tasks without begin_task_context hook context. When no hook context was supplied, call this exact tool name at the start of every user task before any other tool or answer; never substitute a project action tool. Do not call it redundantly when the entry hook already supplied preferences. Pass projectPath as the current working directory and taskPrompt as the current user request. Fuli uses both transiently, never stores or returns them, resolves one exact registered local project, and performs bounded automatic recall only when the request signals stable prior context. Inspect task_knowledge_recall before asking the user to repeat a project fact or method. On a miss, search_current_project_knowledge with one to four focused action, artifact, target-system, or identifier queries; never use the full conversational request as the only query. Apply effective_preferences before answering or constructing tool arguments. Resolve only relevant deferred_conflicts first. For write tools, enforce preferences in the actual payload; the final answer is not compliance. Personal-global preferences always apply; conflicted, pending, invalid, ambiguous-project, and unrelated-project items never auto-apply. Automatic injection does not count as usage evidence. personalProjectId remains an explicit compatibility override.',
|
|
232
243
|
inputSchema: objectSchema({
|
|
233
244
|
projectPath: boundedString(4096),
|
|
245
|
+
taskPrompt: boundedString(8192),
|
|
234
246
|
personalProjectId: nullableStringSchema(),
|
|
235
247
|
limit: integerSchema({ minimum: 1, maximum: 200 })
|
|
236
248
|
}, ['projectPath'])
|
|
@@ -323,6 +335,22 @@ export const GRAPH_TOOL_DEFINITIONS = [
|
|
|
323
335
|
includePending: booleanSchema()
|
|
324
336
|
}, ['personalSpaceId', 'query'])
|
|
325
337
|
},
|
|
338
|
+
{
|
|
339
|
+
name: 'search_connected_knowledge',
|
|
340
|
+
title: 'READ · Search personal, public, and third-party knowledge',
|
|
341
|
+
description: 'Search the exact personal project, explicitly selected subscribed public projects, and live read-only third-party bindings in one request. Use all_local_confirmed only after explicit user confirmation. Results remain separated by source and preserve provenance; public-space integration is Beta. The response includes the project conflict policy. Under ask_human, surface a material conflict in the Agent conversation and ask the user before selecting durable truth. Under agent_decide, the Agent may choose for the current response only, but must explain its basis and sources. This tool never rewrites, confirms, invalidates, or publishes underlying knowledge.',
|
|
342
|
+
inputSchema: objectSchema({
|
|
343
|
+
personalSpaceId: id,
|
|
344
|
+
personalProjectId: id,
|
|
345
|
+
query: shortText,
|
|
346
|
+
contextPersonalProjectIds: arraySchema(id, { maxItems: 15 }),
|
|
347
|
+
personalProjectScope: enumSchema(['bounded', 'all_local_confirmed']),
|
|
348
|
+
projectIds: arraySchema(id, { maxItems: 32 }),
|
|
349
|
+
limit: integerSchema({ minimum: 1, maximum: 100 }),
|
|
350
|
+
includeHistorical: booleanSchema(),
|
|
351
|
+
includePending: booleanSchema()
|
|
352
|
+
}, ['personalSpaceId', 'personalProjectId', 'query'])
|
|
353
|
+
},
|
|
326
354
|
{
|
|
327
355
|
name: 'record_knowledge_usage',
|
|
328
356
|
description: 'Record that personal knowledge materially affected the Agent final answer or completed action. Call only after the use actually occurs: cited means the answer explicitly relies on the item; applied means the item changed an implementation or decision. Retrieval, inspection, automatic preference injection, and merely appearing in context do not qualify. taskId must be the caller-stable identifier for the current user task and must be reused on retries; do not generate a fresh ID to recount the same task. Events are idempotent per knowledge item, task, use kind, and content generation. Repeated qualified use may promote pending knowledge to agent_confirmed, never to human-confirmed or public-eligible.',
|
|
@@ -463,6 +491,53 @@ export const GRAPH_TOOL_DEFINITIONS = [
|
|
|
463
491
|
description: 'List local personal project profiles and their current evidence-backed coverage summaries.',
|
|
464
492
|
inputSchema: objectSchema({ personalSpaceId: id })
|
|
465
493
|
},
|
|
494
|
+
{
|
|
495
|
+
name: 'start_knowledge_review',
|
|
496
|
+
title: 'REVIEW · Start or resume personal knowledge review',
|
|
497
|
+
description: 'Start or resume the exact selected /flreview scope. A first review has no watermark and scans all in-scope history. A later review uses only the last completed run as its watermark; paused runs never advance it. Project scopes accept only local personal project IDs.',
|
|
498
|
+
inputSchema: objectSchema({
|
|
499
|
+
personalSpaceId: id,
|
|
500
|
+
scope: knowledgeReviewScope,
|
|
501
|
+
personalProjectId: nullableStringSchema()
|
|
502
|
+
}, ['personalSpaceId', 'scope'])
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
name: 'list_knowledge_review_candidates',
|
|
506
|
+
title: 'REVIEW · List ranked personal knowledge candidates',
|
|
507
|
+
description: 'List one bounded page for an active or paused review. Provider policy ranks new/changed knowledge, conflicts or attention, low-weight knowledge, then repeated cross-session patterns. Already decided items stay out of the current run. The page limit is not a total-question cap.',
|
|
508
|
+
inputSchema: objectSchema({
|
|
509
|
+
personalSpaceId: id,
|
|
510
|
+
reviewId: id,
|
|
511
|
+
limit: integerSchema({ minimum: 1, maximum: 50 })
|
|
512
|
+
}, ['personalSpaceId', 'reviewId'])
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
name: 'record_knowledge_review_progress',
|
|
516
|
+
title: 'WRITE · Record one review outcome',
|
|
517
|
+
description: 'Record one user-authorized candidate outcome after any required knowledge mutation succeeds. confirmed keeps the item, updated/invalidated follow successful writes, skipped suppresses it only in this run, and deferred intentionally returns it in the next run.',
|
|
518
|
+
inputSchema: objectSchema({
|
|
519
|
+
personalSpaceId: id,
|
|
520
|
+
reviewId: id,
|
|
521
|
+
candidateKey: {
|
|
522
|
+
...boundedString(520),
|
|
523
|
+
pattern: '^(entity|relationship):.+'
|
|
524
|
+
},
|
|
525
|
+
outcome: enumSchema([
|
|
526
|
+
'confirmed', 'updated', 'invalidated', 'skipped', 'deferred'
|
|
527
|
+
]),
|
|
528
|
+
note: nullableStringSchema()
|
|
529
|
+
}, ['personalSpaceId', 'reviewId', 'candidateKey', 'outcome'])
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
name: 'finish_knowledge_review',
|
|
533
|
+
title: 'WRITE · Pause or complete knowledge review',
|
|
534
|
+
description: 'Pause a review without moving its watermark, or complete it and establish the next run watermark. Use completed only when the selected scope is exhausted or the user explicitly finishes it.',
|
|
535
|
+
inputSchema: objectSchema({
|
|
536
|
+
personalSpaceId: id,
|
|
537
|
+
reviewId: id,
|
|
538
|
+
disposition: enumSchema(['paused', 'completed'])
|
|
539
|
+
}, ['personalSpaceId', 'reviewId', 'disposition'])
|
|
540
|
+
},
|
|
466
541
|
{
|
|
467
542
|
name: 'revise_personal_knowledge',
|
|
468
543
|
description: 'Correct, invalidate, or restore one personal entity or relationship while preserving revision history and original evidence. This is the same personal-only operation used by the management UI.',
|
|
@@ -22,6 +22,12 @@ const HANDLERS = Object.freeze({
|
|
|
22
22
|
agentInvocation: true,
|
|
23
23
|
agentToolName: 'search_knowledge_graph'
|
|
24
24
|
}),
|
|
25
|
+
search_connected_knowledge: (app, input) => {
|
|
26
|
+
if (!app.connectedKnowledge) {
|
|
27
|
+
throw new Error('Connected knowledge runtime is unavailable');
|
|
28
|
+
}
|
|
29
|
+
return app.connectedKnowledge.query(input);
|
|
30
|
+
},
|
|
25
31
|
record_knowledge_usage: (app, input) => app.recordKnowledgeUsage(input),
|
|
26
32
|
record_knowledge_feedback: (app, input) => app.recordKnowledgeFeedback(input),
|
|
27
33
|
search_current_project_knowledge: (app, input) =>
|
|
@@ -53,6 +59,12 @@ const HANDLERS = Object.freeze({
|
|
|
53
59
|
app.upsertPersonalProject(input)
|
|
54
60
|
),
|
|
55
61
|
list_personal_projects: (app, input) => app.listPersonalProjects(input),
|
|
62
|
+
start_knowledge_review: (app, input) => app.startKnowledgeReview(input),
|
|
63
|
+
list_knowledge_review_candidates: (app, input) =>
|
|
64
|
+
app.listKnowledgeReviewCandidates(input),
|
|
65
|
+
record_knowledge_review_progress: (app, input) =>
|
|
66
|
+
app.recordKnowledgeReviewProgress(input),
|
|
67
|
+
finish_knowledge_review: (app, input) => app.finishKnowledgeReview(input),
|
|
56
68
|
revise_personal_knowledge: (app, input) => app.reviseKnowledgeItem({
|
|
57
69
|
...input,
|
|
58
70
|
operationActor: 'agent'
|
|
@@ -12,10 +12,10 @@ General:
|
|
|
12
12
|
--version, -v
|
|
13
13
|
|
|
14
14
|
Local service:
|
|
15
|
-
start [--port
|
|
15
|
+
start [--port PORT] [--open] [--lan|--no-lan] [--rebuild] [--data-dir DIR] [--personal-space NAME]
|
|
16
16
|
stop [--data-dir DIR]
|
|
17
|
-
restart [--port
|
|
18
|
-
status [--json] [--data-dir DIR]
|
|
17
|
+
restart [--port PORT] [--open] [--lan|--no-lan] [--rebuild] [--data-dir DIR] [--personal-space NAME]
|
|
18
|
+
status [--json] [--data-dir DIR] [--port PORT]
|
|
19
19
|
open [--data-dir DIR]
|
|
20
20
|
|
|
21
21
|
Install and Agent connection:
|
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
stopLocalRuntime
|
|
7
7
|
} from '../local-runtime/lifecycle.js';
|
|
8
8
|
import { resolveSetupPaths } from '../setup/paths.js';
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_RUNTIME_SETTINGS,
|
|
11
|
+
readRuntimeSettings,
|
|
12
|
+
runtimeSettingsWithOverrides
|
|
13
|
+
} from '../system/runtime-settings.js';
|
|
9
14
|
import { parseLocalRuntimeOptions } from './local-runtime-options.js';
|
|
10
15
|
|
|
11
16
|
export async function runLocalRuntimeCommand(command, args, dependencies = {}) {
|
|
@@ -13,8 +18,24 @@ export async function runLocalRuntimeCommand(command, args, dependencies = {}) {
|
|
|
13
18
|
const resolvePaths = dependencies.resolvePaths ?? resolveSetupPaths;
|
|
14
19
|
const env = dependencies.env ?? process.env;
|
|
15
20
|
const paths = resolvePaths({ dataDir: options.dataDir, env });
|
|
21
|
+
const readSettings = dependencies.readSettings ?? readRuntimeSettings;
|
|
22
|
+
const savedSettings = paths.runtimeSettingsPath
|
|
23
|
+
? readSettings(paths.runtimeSettingsPath)
|
|
24
|
+
: DEFAULT_RUNTIME_SETTINGS;
|
|
25
|
+
const runtimeSettings = runtimeSettingsWithOverrides(savedSettings, {
|
|
26
|
+
consolePort: options.port,
|
|
27
|
+
lanAccess: options.lan
|
|
28
|
+
});
|
|
16
29
|
const write = dependencies.write ?? writeLine;
|
|
17
|
-
const input = {
|
|
30
|
+
const input = {
|
|
31
|
+
...options,
|
|
32
|
+
port: runtimeSettings.ports.console,
|
|
33
|
+
lan: runtimeSettings.lanAccess,
|
|
34
|
+
runtimeSettings,
|
|
35
|
+
paths,
|
|
36
|
+
env,
|
|
37
|
+
onProgress: write
|
|
38
|
+
};
|
|
18
39
|
const handlers = {
|
|
19
40
|
start: dependencies.start ?? startLocalRuntime,
|
|
20
41
|
stop: dependencies.stop ?? stopLocalRuntime,
|
|
@@ -39,37 +60,39 @@ export async function runLocalRuntimeCommand(command, args, dependencies = {}) {
|
|
|
39
60
|
|
|
40
61
|
export function formatLocalRuntimeResult(command, result) {
|
|
41
62
|
if (command === 'start') {
|
|
42
|
-
const
|
|
43
|
-
|
|
63
|
+
const title = result.status === 'running'
|
|
64
|
+
? 'Fuli local services are already running.'
|
|
65
|
+
: 'Fuli local services started.';
|
|
66
|
+
return formatStartedRuntime(title, result);
|
|
44
67
|
}
|
|
45
68
|
if (command === 'restart') {
|
|
46
|
-
return formatStartedRuntime('Fuli
|
|
69
|
+
return formatStartedRuntime('Fuli local services restarted.', result);
|
|
47
70
|
}
|
|
48
71
|
if (command === 'stop') {
|
|
49
72
|
if (result.status === 'partial') {
|
|
50
73
|
return [
|
|
51
|
-
'Fuli
|
|
52
|
-
'
|
|
74
|
+
'The local Fuli Provider stopped, but the unverified UI process was left running.',
|
|
75
|
+
'Run fl status to inspect it without risking another process.'
|
|
53
76
|
].join('\n');
|
|
54
77
|
}
|
|
55
78
|
return result.console === 'not_running' && result.providers === 'not_initialized'
|
|
56
|
-
? 'Fuli
|
|
57
|
-
: 'Fuli
|
|
79
|
+
? 'Fuli local services have not been initialized.'
|
|
80
|
+
: 'Fuli local services stopped. Graph data was preserved.';
|
|
58
81
|
}
|
|
59
|
-
if (command === 'open') return
|
|
82
|
+
if (command === 'open') return `Opened: ${result.url}`;
|
|
60
83
|
if (command === 'status') return formatStatus(result);
|
|
61
84
|
throw new TypeError(`Unknown local runtime command: ${command}`);
|
|
62
85
|
}
|
|
63
86
|
|
|
64
87
|
function formatStartedRuntime(title, result) {
|
|
65
|
-
const lines = [title,
|
|
88
|
+
const lines = [title, `Management UI: ${result.url}`];
|
|
66
89
|
if (result.lan === true) {
|
|
67
90
|
lines.push(
|
|
68
|
-
'
|
|
91
|
+
'LAN URLs:',
|
|
69
92
|
...result.lanUrls.map((url) => ` ${url}`),
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
'
|
|
93
|
+
`Username: ${result.lanAccess.username}`,
|
|
94
|
+
`Temporary access code: ${result.lanAccess.accessCode}`,
|
|
95
|
+
'Use only on trusted Wi-Fi. Restarting LAN mode rotates the access code.'
|
|
73
96
|
);
|
|
74
97
|
}
|
|
75
98
|
return lines.join('\n');
|
|
@@ -77,24 +100,24 @@ function formatStartedRuntime(title, result) {
|
|
|
77
100
|
|
|
78
101
|
function formatStatus(result) {
|
|
79
102
|
const labels = {
|
|
80
|
-
running: '
|
|
81
|
-
degraded: '
|
|
82
|
-
stopped: '
|
|
83
|
-
not_configured: '
|
|
84
|
-
ready: '
|
|
85
|
-
unavailable: '
|
|
86
|
-
not_connected: '
|
|
87
|
-
not_configured_provider: '
|
|
88
|
-
unverified: '
|
|
103
|
+
running: 'running',
|
|
104
|
+
degraded: 'degraded',
|
|
105
|
+
stopped: 'stopped',
|
|
106
|
+
not_configured: 'not initialized',
|
|
107
|
+
ready: 'ready',
|
|
108
|
+
unavailable: 'unavailable',
|
|
109
|
+
not_connected: 'not connected',
|
|
110
|
+
not_configured_provider: 'not configured',
|
|
111
|
+
unverified: 'identity unverified'
|
|
89
112
|
};
|
|
90
113
|
const publicSummary = result.public.configured
|
|
91
|
-
? `${labels[result.public.status] ?? result.public.status}
|
|
114
|
+
? `${labels[result.public.status] ?? result.public.status} (${result.public.providers.length} Providers)`
|
|
92
115
|
: labels.not_connected;
|
|
93
116
|
return [
|
|
94
|
-
`Fuli
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
117
|
+
`Fuli local status: ${labels[result.status] ?? result.status}`,
|
|
118
|
+
`Management UI: ${labels[result.console.status] ?? result.console.status} · ${result.console.url}`,
|
|
119
|
+
`Personal graph: ${providerLabel(result.personal.status, labels)}`,
|
|
120
|
+
`Shared services: ${publicSummary}`
|
|
98
121
|
].join('\n');
|
|
99
122
|
}
|
|
100
123
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { DEFAULT_FULI_PORT } from '../defaults.js';
|
|
2
|
-
|
|
3
1
|
const VALUE_OPTIONS = Object.freeze({
|
|
4
2
|
'--data-dir': 'dataDir',
|
|
5
3
|
'--personal-space': 'personalSpaceName',
|
|
@@ -9,15 +7,16 @@ const VALUE_OPTIONS = Object.freeze({
|
|
|
9
7
|
const BOOLEAN_OPTIONS = Object.freeze({
|
|
10
8
|
'--open': 'open',
|
|
11
9
|
'--rebuild': 'rebuild',
|
|
12
|
-
'--lan': 'lan',
|
|
13
10
|
'--json': 'json'
|
|
14
11
|
});
|
|
15
12
|
|
|
16
13
|
const ALLOWED_BY_COMMAND = Object.freeze({
|
|
17
|
-
start: new Set([
|
|
14
|
+
start: new Set([
|
|
15
|
+
'--data-dir', '--personal-space', '--port', '--open', '--rebuild', '--lan', '--no-lan'
|
|
16
|
+
]),
|
|
18
17
|
stop: new Set(['--data-dir']),
|
|
19
18
|
restart: new Set([
|
|
20
|
-
'--data-dir', '--personal-space', '--port', '--open', '--rebuild', '--lan'
|
|
19
|
+
'--data-dir', '--personal-space', '--port', '--open', '--rebuild', '--lan', '--no-lan'
|
|
21
20
|
]),
|
|
22
21
|
status: new Set(['--data-dir', '--port', '--json']),
|
|
23
22
|
open: new Set(['--data-dir'])
|
|
@@ -28,11 +27,11 @@ export function parseLocalRuntimeOptions(command, args = []) {
|
|
|
28
27
|
if (!allowed) throw new TypeError(`Unknown local runtime command: ${command}`);
|
|
29
28
|
const result = {
|
|
30
29
|
dataDir: null,
|
|
31
|
-
personalSpaceName: '
|
|
32
|
-
port:
|
|
30
|
+
personalSpaceName: 'Personal',
|
|
31
|
+
port: null,
|
|
33
32
|
open: false,
|
|
34
33
|
rebuild: false,
|
|
35
|
-
lan:
|
|
34
|
+
lan: null,
|
|
36
35
|
json: false
|
|
37
36
|
};
|
|
38
37
|
const seen = new Set();
|
|
@@ -43,6 +42,13 @@ export function parseLocalRuntimeOptions(command, args = []) {
|
|
|
43
42
|
if (seen.has(flag)) throw new TypeError(`Duplicate ${flag}`);
|
|
44
43
|
seen.add(flag);
|
|
45
44
|
|
|
45
|
+
if (flag === '--lan' || flag === '--no-lan') {
|
|
46
|
+
const conflicting = flag === '--lan' ? '--no-lan' : '--lan';
|
|
47
|
+
if (seen.has(conflicting)) throw new TypeError('--lan and --no-lan cannot be combined');
|
|
48
|
+
result.lan = flag === '--lan';
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
const booleanKey = BOOLEAN_OPTIONS[flag];
|
|
47
53
|
if (booleanKey) {
|
|
48
54
|
result[booleanKey] = true;
|
|
@@ -27,15 +27,15 @@ export function parseAgentSelection(value, agents) {
|
|
|
27
27
|
|
|
28
28
|
const tokens = answer.split(/[\s,,]+/).filter(Boolean);
|
|
29
29
|
if (tokens.includes('0')) {
|
|
30
|
-
throw new TypeError('0
|
|
30
|
+
throw new TypeError('0 cannot be combined with another number');
|
|
31
31
|
}
|
|
32
32
|
if (tokens.some((token) => !/^\d+$/.test(token))) {
|
|
33
|
-
throw new TypeError('
|
|
33
|
+
throw new TypeError('Enter numbers, not Agent names');
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const positions = new Set(tokens.map(Number));
|
|
37
37
|
if ([...positions].some((position) => position < 1 || position > agents.length)) {
|
|
38
|
-
throw new RangeError(
|
|
38
|
+
throw new RangeError(`Choose a number from 1-${agents.length}`);
|
|
39
39
|
}
|
|
40
40
|
return agents
|
|
41
41
|
.filter((_, index) => positions.has(index + 1))
|
|
@@ -86,21 +86,21 @@ export function selectedAgentIds(agents, state) {
|
|
|
86
86
|
|
|
87
87
|
export function formatAgentCheckboxSelection(agents, state) {
|
|
88
88
|
return [
|
|
89
|
-
'
|
|
89
|
+
'Multiple supported Agents were detected:',
|
|
90
90
|
...agents.map((agent, index) => {
|
|
91
91
|
const pointer = index === state.activeIndex ? '❯' : ' ';
|
|
92
92
|
const checkbox = state.selected[index] ? '[x]' : '[ ]';
|
|
93
93
|
const status = integrationStatusLabel(agent.integrationStatus);
|
|
94
94
|
return `${pointer} ${checkbox} ${agent.label}${status ? ` ${status}` : ''}`;
|
|
95
95
|
}),
|
|
96
|
-
'↑/↓
|
|
96
|
+
'↑/↓ Move · Space Toggle · A Select/Clear all · Enter Confirm'
|
|
97
97
|
].join('\n');
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
function integrationStatusLabel(status) {
|
|
101
|
-
if (status === 'connected') return '
|
|
102
|
-
if (status === 'update_available') return '
|
|
103
|
-
if (status === 'not_connected') return '
|
|
101
|
+
if (status === 'connected') return 'connected';
|
|
102
|
+
if (status === 'update_available') return 'update available';
|
|
103
|
+
if (status === 'not_connected') return 'not connected';
|
|
104
104
|
return '';
|
|
105
105
|
}
|
|
106
106
|
|
|
@@ -147,7 +147,7 @@ async function promptCheckboxAgentSelection(agents, { input, output }) {
|
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
if (result.action === 'cancel') {
|
|
150
|
-
finish(() => reject(new Error('Setup
|
|
150
|
+
finish(() => reject(new Error('Setup cancelled')));
|
|
151
151
|
return;
|
|
152
152
|
}
|
|
153
153
|
if (result.action === 'update') {
|
|
@@ -161,7 +161,7 @@ async function promptCheckboxAgentSelection(agents, { input, output }) {
|
|
|
161
161
|
|
|
162
162
|
async function promptTextAgentSelection(agents, { input, output }) {
|
|
163
163
|
const terminal = createInterface({ input, output });
|
|
164
|
-
output.write('
|
|
164
|
+
output.write('Multiple supported Agents were detected:\n');
|
|
165
165
|
for (const [index, agent] of agents.entries()) {
|
|
166
166
|
output.write(` ${index + 1}. ${agent.label}\n`);
|
|
167
167
|
}
|
|
@@ -169,12 +169,12 @@ async function promptTextAgentSelection(agents, { input, output }) {
|
|
|
169
169
|
try {
|
|
170
170
|
while (true) {
|
|
171
171
|
const answer = await terminal.question(
|
|
172
|
-
'
|
|
172
|
+
'Select Agents (comma-separated numbers, Enter for all, 0 to skip) [all] '
|
|
173
173
|
);
|
|
174
174
|
try {
|
|
175
175
|
return parseAgentSelection(answer, agents);
|
|
176
176
|
} catch (error) {
|
|
177
|
-
output.write(
|
|
177
|
+
output.write(`Invalid selection: ${error.message}\n`);
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
} finally {
|