deepvista-cli 0.1.18__tar.gz → 0.2.0__tar.gz
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.
- deepvista_cli-0.2.0/.release-please-manifest.json +3 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/CHANGELOG.md +8 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/PKG-INFO +1 -1
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/agent_catalog.py +22 -9
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/session.py +24 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/config.py +57 -2
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/README.md +19 -0
- deepvista_cli-0.2.0/plugins/claude-code/commands/deepvista.md +129 -0
- deepvista_cli-0.2.0/plugins/claude-code/skills/daily-planning/SKILL.md +161 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/pyproject.toml +1 -1
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/session.md +31 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/uv.lock +1 -1
- deepvista_cli-0.1.18/.release-please-manifest.json +0 -3
- deepvista_cli-0.1.18/tests/test_agent_catalog.py +0 -225
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/.claude-plugin/marketplace.json +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/.github/workflows/ci.yml +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/.github/workflows/publish.yml +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/.github/workflows/release-please.yml +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/.gitignore +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/.pre-commit-config.yaml +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/CLAUDE.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/CONTRIBUTING.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/LICENSE +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/README.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/__init__.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/auth/__init__.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/auth/callback_server.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/auth/login.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/auth/tokens.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/client/__init__.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/client/http.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/client/origin.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/__init__.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/agents.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/auth.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/card.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/chat.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/config.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/lint.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/memory.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/notes.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/skill.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/upgrade.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/commands/vistabase.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/main.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/output/__init__.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/output/formatter.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/resources/__init__.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/resources/workflow_host_runtime.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/session_note.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/skill_catalog.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/tui/__init__.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/tui/app.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/workflow_doc.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/docs/assets/deepvista-banner.png +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/install.sh +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/README.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/agents/.gitignore +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/commands/refresh-skills.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/hooks/hooks.json +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/scripts/deepvista-session-end.sh +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/scripts/deepvista-session-start.sh +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/scripts/deepvista-session-turn.sh +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/scripts/deepvista-skill-url.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/scripts/deepvista-sync.sh +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/skills/.gitignore +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/skills/install-deepvista-cli/SKILL.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/release-please-config.json +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/scripts/check_plugin_version.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/SKILL.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/chat.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/lint.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/memory.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/notes.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/openclaw.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/shared.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/skill-analyze-notes.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/skill-create-from-note.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/skill-import-files.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/skill-research-to-skill.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/skill.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/vistabase-card.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/vistabase.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/dv-workflow/SKILL.md +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/tests/__init__.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/tests/test_agent_id_tagging.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/tests/test_session_note_format.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/tests/test_skill_catalog.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/tests/test_skill_commands.py +0 -0
- {deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/uninstall.sh +0 -0
|
@@ -37,6 +37,14 @@ users what's new between the version they have installed and the latest release.
|
|
|
37
37
|
adopts a pre-existing server-side row instead of failing when the local
|
|
38
38
|
file is missing.
|
|
39
39
|
|
|
40
|
+
## [0.2.0](https://github.com/DeepVista-AI/deepvista-cli/compare/v0.1.18...v0.2.0) (2026-05-28)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Features
|
|
44
|
+
|
|
45
|
+
* **DV-853:** productionize the deepvista subagents ([#147](https://github.com/DeepVista-AI/deepvista-cli/issues/147)) ([45e9ba1](https://github.com/DeepVista-AI/deepvista-cli/commit/45e9ba17de08243fe84be3de6826c72116360351))
|
|
46
|
+
* **session:** skip session init for configured CWD patterns (DV-862) ([#148](https://github.com/DeepVista-AI/deepvista-cli/issues/148)) ([df74381](https://github.com/DeepVista-AI/deepvista-cli/commit/df743810ae6dec7c4a520c43d82f1eeae421762b))
|
|
47
|
+
|
|
40
48
|
## [0.1.18](https://github.com/DeepVista-AI/deepvista-cli/compare/v0.1.17...v0.1.18) (2026-05-28)
|
|
41
49
|
|
|
42
50
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepvista-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: CLI for DeepVista — chat, notes, skills, and memory from your terminal.
|
|
5
5
|
Project-URL: Homepage, https://deepvista.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/DeepVista-AI/deepvista-cli
|
|
@@ -105,7 +105,12 @@ class AgentRoleMeta:
|
|
|
105
105
|
agent_type: str = "" # e.g. "claude-code"
|
|
106
106
|
updated_at: str = ""
|
|
107
107
|
count: int = 1 # how many managed agents share this role
|
|
108
|
-
|
|
108
|
+
# Plain-text system prompt baked verbatim as the subagent body. Sourced
|
|
109
|
+
# from the managed agent's ``config.system_prompt`` (preferred) or its
|
|
110
|
+
# older ``config.soul`` (fallback). Persona-skill references are NOT
|
|
111
|
+
# resolved at export time — the prompt should instruct the agent to load
|
|
112
|
+
# the persona itself (e.g. "follow persona context card xxxx").
|
|
113
|
+
system_prompt: str = ""
|
|
109
114
|
|
|
110
115
|
@property
|
|
111
116
|
def slug(self) -> str:
|
|
@@ -207,14 +212,17 @@ def build_agent_markdown(meta: AgentRoleMeta) -> str:
|
|
|
207
212
|
frontmatter.append(f"x-deepvista-updated-at: {meta.updated_at}")
|
|
208
213
|
frontmatter.append("---")
|
|
209
214
|
|
|
210
|
-
# A custom system prompt (config.
|
|
211
|
-
#
|
|
215
|
+
# A custom system prompt (config.system_prompt, falling back to config.soul)
|
|
216
|
+
# is authoritative — bake it in as the body verbatim. Frontmatter (routing,
|
|
217
|
+
# tools, model, preloaded skill) stays templated. The prompt itself is the
|
|
218
|
+
# right place to reference a persona context card by id ("…follow persona
|
|
219
|
+
# context card xxxx"); the agent loads it at runtime via the deepvista skill.
|
|
212
220
|
if meta.system_prompt.strip():
|
|
213
221
|
prompt_body = (
|
|
214
|
-
"<!-- Generated by `deepvista agents export` from the configured system
|
|
215
|
-
f"
|
|
216
|
-
f" · agent id: {meta.agent_id or 'n/a'}. Edit it in DeepVista — local
|
|
217
|
-
" are overwritten on the next sync. -->\n\n"
|
|
222
|
+
"<!-- Generated by `deepvista agents export` from the configured system\n"
|
|
223
|
+
f" prompt of your “{meta.agent_name}” managed agent · role: {meta.role}\n"
|
|
224
|
+
f" · agent id: {meta.agent_id or 'n/a'}. Edit it in DeepVista — local\n"
|
|
225
|
+
" changes are overwritten on the next sync. -->\n\n"
|
|
218
226
|
f"{meta.system_prompt.strip()}\n"
|
|
219
227
|
)
|
|
220
228
|
return "\n".join(frontmatter) + "\n\n" + prompt_body
|
|
@@ -290,7 +298,12 @@ def metas_from_agents(agents: list[dict[str, Any]]) -> list[AgentRoleMeta]:
|
|
|
290
298
|
continue
|
|
291
299
|
updated = str(agent.get("updated_at") or "")
|
|
292
300
|
config = agent.get("config") or {}
|
|
293
|
-
|
|
301
|
+
# DV-853: ``config.system_prompt`` is the public field for the
|
|
302
|
+
# subagent body. Falls back to the legacy ``config.soul`` so existing
|
|
303
|
+
# managed agents keep working unchanged. Plain text; persona-card
|
|
304
|
+
# references are left to the prompt's wording (the agent loads them
|
|
305
|
+
# at runtime via the preloaded `deepvista` skill).
|
|
306
|
+
prompt_body = str(config.get("system_prompt") or config.get("soul") or "").strip()
|
|
294
307
|
candidate = AgentRoleMeta(
|
|
295
308
|
role=raw_role,
|
|
296
309
|
agent_name=str(agent.get("name") or ""),
|
|
@@ -298,7 +311,7 @@ def metas_from_agents(agents: list[dict[str, Any]]) -> list[AgentRoleMeta]:
|
|
|
298
311
|
agent_type=str(agent.get("agent_type") or ""),
|
|
299
312
|
updated_at=updated,
|
|
300
313
|
count=1,
|
|
301
|
-
system_prompt=
|
|
314
|
+
system_prompt=prompt_body,
|
|
302
315
|
)
|
|
303
316
|
existing = by_role.get(slug)
|
|
304
317
|
if existing is None:
|
|
@@ -23,6 +23,7 @@ import click
|
|
|
23
23
|
from deepvista_cli import session_note as sn
|
|
24
24
|
from deepvista_cli.client.http import DeepVistaClient
|
|
25
25
|
from deepvista_cli.client.origin import detect_agent_tool
|
|
26
|
+
from deepvista_cli.config import should_skip_session_cwd
|
|
26
27
|
from deepvista_cli.output.formatter import format_output, output_error
|
|
27
28
|
|
|
28
29
|
SESSION_CARD_TYPE = "session"
|
|
@@ -116,6 +117,29 @@ def session_init(
|
|
|
116
117
|
"""
|
|
117
118
|
from deepvista_cli.commands.agents import load_agent_id_for_active_agent
|
|
118
119
|
|
|
120
|
+
if should_skip_session_cwd(cwd):
|
|
121
|
+
# DV-862: CWD matches ``session_skip_cwd_patterns`` in
|
|
122
|
+
# ``~/.config/deepvista/config.json``. Drops sub-agent sessions
|
|
123
|
+
# whose CWD isn't a real working directory — e.g. claude-mem's
|
|
124
|
+
# observer sub-claude runs with
|
|
125
|
+
# ``cwd=~/.claude-mem/observer-sessions`` and quotes file paths
|
|
126
|
+
# from the *primary* session, polluting the vistabase. Downstream
|
|
127
|
+
# ``tick`` / ``finalize`` self-no-op (no cached card → exit 3) so
|
|
128
|
+
# guarding ``init`` alone covers the whole lifecycle.
|
|
129
|
+
format_output(
|
|
130
|
+
{
|
|
131
|
+
"skipped": True,
|
|
132
|
+
"reason": "cwd matches session_skip_cwd_patterns",
|
|
133
|
+
"cwd": cwd,
|
|
134
|
+
"session_id": session_id,
|
|
135
|
+
},
|
|
136
|
+
ctx.obj.output_format,
|
|
137
|
+
title="Session init (skipped)",
|
|
138
|
+
entity_type=SESSION_ENTITY_TYPE,
|
|
139
|
+
base_url=ctx.obj.auth_url,
|
|
140
|
+
)
|
|
141
|
+
return
|
|
142
|
+
|
|
119
143
|
if agent is None:
|
|
120
144
|
detected_agent, detected_version = detect_agent_tool()
|
|
121
145
|
agent = detected_agent
|
|
@@ -8,6 +8,7 @@ Resolution order for each setting:
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
+
import fnmatch
|
|
11
12
|
import json
|
|
12
13
|
import os
|
|
13
14
|
from dataclasses import dataclass
|
|
@@ -36,6 +37,21 @@ def credentials_path(profile: str = "default") -> Path:
|
|
|
36
37
|
|
|
37
38
|
PROFILES_PATH = CONFIG_DIR / "config.json"
|
|
38
39
|
|
|
40
|
+
# Reserved top-level key in ``config.json`` holding the list of CWD globs
|
|
41
|
+
# that ``deepvista session init`` should skip. Stored as a flat list (not a
|
|
42
|
+
# dict) so it's trivially distinguishable from profile entries and easy for
|
|
43
|
+
# the user to hand-edit. See DV-862.
|
|
44
|
+
SESSION_SKIP_CWD_KEY = "session_skip_cwd_patterns"
|
|
45
|
+
|
|
46
|
+
# Defaults used when ``config.json`` is missing the key. Skips claude-mem's
|
|
47
|
+
# observer sub-claude (cwd=~/.claude-mem/observer-sessions) so its quoted
|
|
48
|
+
# file paths from the *primary* session don't get mined into bogus File
|
|
49
|
+
# cards (root cause for DV-861).
|
|
50
|
+
DEFAULT_SESSION_SKIP_CWD_PATTERNS: tuple[str, ...] = (
|
|
51
|
+
"*/.claude-mem/observer-sessions",
|
|
52
|
+
"*/.claude-mem/observer-sessions/*",
|
|
53
|
+
)
|
|
54
|
+
|
|
39
55
|
|
|
40
56
|
# ---------------------------------------------------------------------------
|
|
41
57
|
# Exit codes (following GWS pattern)
|
|
@@ -84,8 +100,13 @@ def set_profile(name: str, settings: dict) -> None:
|
|
|
84
100
|
|
|
85
101
|
|
|
86
102
|
def list_profiles() -> dict:
|
|
87
|
-
"""List all profiles.
|
|
88
|
-
|
|
103
|
+
"""List all profiles.
|
|
104
|
+
|
|
105
|
+
Filters out reserved non-profile keys (e.g.
|
|
106
|
+
``session_skip_cwd_patterns``) by keeping only dict-valued entries —
|
|
107
|
+
profiles are always dicts, reserved settings are lists/scalars.
|
|
108
|
+
"""
|
|
109
|
+
return {k: v for k, v in _load_profiles().items() if isinstance(v, dict)}
|
|
89
110
|
|
|
90
111
|
|
|
91
112
|
def delete_profile(name: str) -> bool:
|
|
@@ -98,6 +119,40 @@ def delete_profile(name: str) -> bool:
|
|
|
98
119
|
return False
|
|
99
120
|
|
|
100
121
|
|
|
122
|
+
# ---------------------------------------------------------------------------
|
|
123
|
+
# Session CWD skip patterns (DV-862)
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_session_skip_cwd_patterns() -> list[str]:
|
|
128
|
+
"""Return the configured CWD-skip patterns for ``session init``.
|
|
129
|
+
|
|
130
|
+
Reads a top-level ``session_skip_cwd_patterns`` list from
|
|
131
|
+
``~/.config/deepvista/config.json``. There is no matching setter — edit
|
|
132
|
+
the file by hand. Falls back to ``DEFAULT_SESSION_SKIP_CWD_PATTERNS``
|
|
133
|
+
when the key is absent. An explicit empty list disables skipping.
|
|
134
|
+
"""
|
|
135
|
+
raw = _load_profiles().get(SESSION_SKIP_CWD_KEY)
|
|
136
|
+
if isinstance(raw, list):
|
|
137
|
+
return [str(p) for p in raw]
|
|
138
|
+
return list(DEFAULT_SESSION_SKIP_CWD_PATTERNS)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def should_skip_session_cwd(cwd: str | None) -> bool:
|
|
142
|
+
"""Return True iff ``cwd`` matches any configured skip pattern.
|
|
143
|
+
|
|
144
|
+
Matching is fnmatch-style (Unix shell globs; ``*`` matches across path
|
|
145
|
+
separators). The hook payload's ``cwd`` is already absolute on every
|
|
146
|
+
supported agent, so no resolution is performed here.
|
|
147
|
+
"""
|
|
148
|
+
if not cwd:
|
|
149
|
+
return False
|
|
150
|
+
for pattern in get_session_skip_cwd_patterns():
|
|
151
|
+
if fnmatch.fnmatchcase(cwd, pattern):
|
|
152
|
+
return True
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
|
|
101
156
|
# ---------------------------------------------------------------------------
|
|
102
157
|
# Runtime config — resolved once per invocation
|
|
103
158
|
# ---------------------------------------------------------------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepvista",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Remote-managed skill catalog from DeepVista. Auto-syncs thin stubs into the plugin's skills dir on SessionStart; full bodies are lazy-loaded at invocation time.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "DeepVista AI",
|
|
@@ -78,6 +78,7 @@ Tunable via environment variables (read by the `SessionStart` hook):
|
|
|
78
78
|
| `${CLAUDE_PLUGIN_ROOT}/agents/dv-<role>.md` | Generated subagent (one per managed-agent role) |
|
|
79
79
|
| `~/.config/deepvista/catalog-state.json` | Last skill-sync timestamp + stub inventory |
|
|
80
80
|
| `~/.config/deepvista/agent-defs-state.json` | Last agent-export timestamp + definition inventory |
|
|
81
|
+
| `~/.config/deepvista/config.json` | CLI profiles + top-level `session_skip_cwd_patterns` list |
|
|
81
82
|
| `~/.config/deepvista/cache/skill-bodies/` | 5-minute TTL cache of fetched bodies |
|
|
82
83
|
| `~/.config/deepvista/logs/catalog-sync.log` | Skill-sync hook stdout/stderr |
|
|
83
84
|
| `~/.config/deepvista/logs/agent-export.log` | Agent-export hook stdout/stderr |
|
|
@@ -102,6 +103,24 @@ DEEPVISTA_FORCE_SYNC=1 deepvista skill sync --force
|
|
|
102
103
|
deepvista agents export --force
|
|
103
104
|
```
|
|
104
105
|
|
|
106
|
+
Nested observer/sub-agent sessions showing up in the vistabase (e.g.
|
|
107
|
+
`observer-sessions · <hash>` notes from claude-mem's observer sub-claude)?
|
|
108
|
+
The plugin's SessionStart hook now skips CWDs matching the top-level
|
|
109
|
+
`session_skip_cwd_patterns` list in `~/.config/deepvista/config.json`.
|
|
110
|
+
Defaults cover `~/.claude-mem/observer-sessions`; to extend, edit the file
|
|
111
|
+
and add patterns (fnmatch-style globs):
|
|
112
|
+
|
|
113
|
+
```jsonc
|
|
114
|
+
{
|
|
115
|
+
"default": { "api_url": "https://api.deepvista.ai" },
|
|
116
|
+
"session_skip_cwd_patterns": [
|
|
117
|
+
"*/.claude-mem/observer-sessions",
|
|
118
|
+
"*/.claude-mem/observer-sessions/*",
|
|
119
|
+
"*/scratchpad/*"
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
105
124
|
No agents showing up as `@<role>`? Confirm you have managed agents with roles:
|
|
106
125
|
|
|
107
126
|
```
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: DeepVista controls — `run` generates + dispatches today's planning note via the daily-planning skill; no args shows help
|
|
3
|
+
argument-hint: "[run]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
DeepVista control surface. Behaviour depends on `$ARGUMENTS`:
|
|
7
|
+
|
|
8
|
+
- **No argument** (or any value other than `run`) → print the help block below.
|
|
9
|
+
- **`run`** → generate today's *Daily Planning* note via the `daily-planning`
|
|
10
|
+
skill (if one doesn't already exist), dispatch each `## <role>` section to
|
|
11
|
+
its matching `@<role>` subagent, and append a consolidated summary back
|
|
12
|
+
onto the note.
|
|
13
|
+
|
|
14
|
+
Planning notes are stored as regular DeepVista notes (`type=note`) tagged
|
|
15
|
+
``daily-planning`` + ``date:YYYYMMDD``. No dedicated `deepvista planning`
|
|
16
|
+
CLI command exists — read/write everything via `deepvista notes` and
|
|
17
|
+
`deepvista card +search`.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## If `$ARGUMENTS` is empty or not `run`
|
|
22
|
+
|
|
23
|
+
Print this verbatim, then stop:
|
|
24
|
+
|
|
25
|
+
> **DeepVista — Claude Code commands**
|
|
26
|
+
>
|
|
27
|
+
> - `/deepvista run` — generate today's *Daily Planning* note (LLM-reasoned,
|
|
28
|
+
> driven by the `daily-planning` skill: yesterday's progress + last 7 days
|
|
29
|
+
> of cards → per-role tasks), then dispatch each `## <role>` section to
|
|
30
|
+
> the matching `@<role>` subagent. Subagent results are appended back to
|
|
31
|
+
> the planning note under a `## Summary — <timestamp>` block.
|
|
32
|
+
> - `/refresh-skills` — resync the DeepVista skill catalog and agent
|
|
33
|
+
> definitions immediately (bypasses the 60-minute throttle).
|
|
34
|
+
>
|
|
35
|
+
> **Tips**
|
|
36
|
+
>
|
|
37
|
+
> - Want to draft a plan without dispatching? Just say *"draft today's
|
|
38
|
+
> planning note"* and the `daily-planning` skill kicks in.
|
|
39
|
+
> - Personalise a subagent's voice by setting `config.system_prompt` on its
|
|
40
|
+
> managed agent (free text, e.g. *"You are the marketing specialist;
|
|
41
|
+
> follow persona context card persona-mkt-001."*) and re-running
|
|
42
|
+
> `/refresh-skills`. The agent loads the persona card at runtime.
|
|
43
|
+
> - Need help with the CLI itself? `deepvista --help` or `deepvista <group> --help`.
|
|
44
|
+
|
|
45
|
+
## If `$ARGUMENTS` is `run`
|
|
46
|
+
|
|
47
|
+
Execute the daily-planning dispatch workflow.
|
|
48
|
+
|
|
49
|
+
### Step 1 — Find today's planning note (or generate one)
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
TODAY=$(date +%Y%m%d)
|
|
53
|
+
deepvista --format json card +search "Daily Planning $TODAY" --limit 5
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Walk the result and pick the card with both ``daily-planning`` and
|
|
57
|
+
``date:$TODAY`` in `tags`. Two cases:
|
|
58
|
+
|
|
59
|
+
- **No match** → today's note doesn't exist. **Load the `daily-planning`
|
|
60
|
+
skill and follow it end-to-end** to produce today's plan. The skill ends
|
|
61
|
+
by saving the note via `deepvista notes create`. Re-run the search above
|
|
62
|
+
to pick up the new note id.
|
|
63
|
+
|
|
64
|
+
- **Match found** → fetch its full body:
|
|
65
|
+
```bash
|
|
66
|
+
deepvista --format json notes get <note-id>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Parse `description` and split on `## ` headings. Treat headings that match
|
|
70
|
+
``Workflow today`` or ``Summary`` (case-insensitive) as reserved; everything
|
|
71
|
+
else is a role section keyed by its heading text (lowercased).
|
|
72
|
+
|
|
73
|
+
### Step 2 — Dispatch each role section to its subagent
|
|
74
|
+
|
|
75
|
+
For each `(role, section_markdown)` in the role sections, invoke the
|
|
76
|
+
matching subagent inline. Skip any role with no on-disk `dv-<role>.md`
|
|
77
|
+
definition — the user hasn't registered a managed agent for it yet.
|
|
78
|
+
Example body:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
@<role>
|
|
82
|
+
|
|
83
|
+
You are dispatched from today's Daily Planning note (id: <note_id>).
|
|
84
|
+
|
|
85
|
+
Your section reads:
|
|
86
|
+
|
|
87
|
+
<section_markdown>
|
|
88
|
+
|
|
89
|
+
Complete it end-to-end. Return your deliverable in the standard subagent
|
|
90
|
+
output format (Frame → Deliverable → Sources → Captured).
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Collect each subagent's full reply.
|
|
94
|
+
|
|
95
|
+
### Step 3 — Append a consolidated summary to the note
|
|
96
|
+
|
|
97
|
+
Build a single markdown block:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
## Summary — <YYYY-MM-DD HH:MM>
|
|
101
|
+
|
|
102
|
+
### @marketing
|
|
103
|
+
<that subagent's reply>
|
|
104
|
+
|
|
105
|
+
### @engineering
|
|
106
|
+
<that subagent's reply>
|
|
107
|
+
|
|
108
|
+
…
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Read the current body, append the block, write it back:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
deepvista --format json notes get <note-id>
|
|
115
|
+
# Capture description from JSON, append the block, then:
|
|
116
|
+
deepvista notes update <note-id> --content-file -
|
|
117
|
+
# (pass `description + appended_block` on stdin)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Step 4 — Report
|
|
121
|
+
|
|
122
|
+
Tell the user, in two lines:
|
|
123
|
+
|
|
124
|
+
- Whether the plan was generated this run (and via which skill), or pulled
|
|
125
|
+
from an existing note.
|
|
126
|
+
- Roles dispatched (and any skipped because no matching subagent existed),
|
|
127
|
+
plus the planning note URL (`https://app.deepvista.ai/notes/<id>`).
|
|
128
|
+
|
|
129
|
+
If any step fails, stop and surface the error — do not silently fall through.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: daily-planning
|
|
3
|
+
description: |
|
|
4
|
+
Generate today's *Daily Planning* note by reading yesterday's plan (and its
|
|
5
|
+
appended progress summary) along with recent context cards from the last 7
|
|
6
|
+
days — then reason about what carries over, what's new, and what each
|
|
7
|
+
registered `@<role>` managed-agent subagent should own today. Save the
|
|
8
|
+
result as a regular DeepVista note tagged ``daily-planning`` +
|
|
9
|
+
``date:YYYYMMDD``. Use when the user runs `/deepvista run` and today's
|
|
10
|
+
planning note doesn't yet exist, or when the user asks to "draft today's
|
|
11
|
+
plan", "make today's standup", or "regenerate my daily planning note".
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Daily planning — generate today's plan from yesterday's context
|
|
15
|
+
|
|
16
|
+
This skill produces today's *Daily Planning* note as an LLM-reasoned plan,
|
|
17
|
+
not a static template. Planning notes are plain `deepvista notes` cards
|
|
18
|
+
(type=note) tagged ``daily-planning`` + ``date:YYYYMMDD`` — there is no
|
|
19
|
+
dedicated `deepvista planning` subcommand. The flow:
|
|
20
|
+
|
|
21
|
+
1. Read **yesterday's** planning note + appended summary (if it exists).
|
|
22
|
+
2. Read the **last 7 days** of context cards (notes, todos, sessions).
|
|
23
|
+
3. List the **`@<role>` subagents** registered for this user.
|
|
24
|
+
4. Reason about carry-over, new tasks, and blockers.
|
|
25
|
+
5. Save the result as a note via `deepvista notes create`.
|
|
26
|
+
|
|
27
|
+
The CLI does the data lookups; this skill is the reasoning runbook.
|
|
28
|
+
|
|
29
|
+
## Step 1 — Read yesterday's plan
|
|
30
|
+
|
|
31
|
+
Search for yesterday's planning note by tag (titles can be edited; tags
|
|
32
|
+
shouldn't). Hybrid search filtered to `type=note` is the most robust:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
YESTERDAY=$(date -v-1d +%Y%m%d 2>/dev/null || date -d 'yesterday' +%Y%m%d)
|
|
36
|
+
TODAY=$(date +%Y%m%d)
|
|
37
|
+
|
|
38
|
+
deepvista --format json card +search "Daily Planning $YESTERDAY" --limit 5
|
|
39
|
+
# If a match is found, fetch its full body:
|
|
40
|
+
# deepvista --format json notes get <id>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Parse `description` (yesterday's full markdown — plan + any
|
|
44
|
+
`## Summary — <timestamp>` blocks that `/deepvista run` appended after
|
|
45
|
+
subagents finished). If nothing matches, treat yesterday as a clean slate.
|
|
46
|
+
|
|
47
|
+
## Step 2 — Read the last 7 days of context cards
|
|
48
|
+
|
|
49
|
+
Each call is bounded (`--limit 20`). Skip silently on failure.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
deepvista --format json notes list --limit 20
|
|
53
|
+
deepvista --format json card list --type todo --limit 20
|
|
54
|
+
deepvista --format json card list --type session --limit 10
|
|
55
|
+
deepvista --format json card +search "progress OR blocker OR shipped OR next" --limit 10
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For each result, keep only items updated in the last 7 days. Extract:
|
|
59
|
+
|
|
60
|
+
- title
|
|
61
|
+
- last-updated date
|
|
62
|
+
- a one-sentence read of what it implies for today
|
|
63
|
+
|
|
64
|
+
## Step 3 — List the registered `@<role>` subagents
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
ls "${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/deepvista}/agents/" 2>/dev/null \
|
|
68
|
+
| grep -E '^dv-.*\.md$' \
|
|
69
|
+
| sed 's/^dv-//; s/\.md$//'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
If the list is empty, default to `marketing,engineering,gtm`. The user can
|
|
73
|
+
register more via `deepvista agents register`.
|
|
74
|
+
|
|
75
|
+
## Step 4 — Reason about today's plan
|
|
76
|
+
|
|
77
|
+
You now have:
|
|
78
|
+
|
|
79
|
+
- Yesterday's plan + appended summaries (assigned vs. shipped).
|
|
80
|
+
- A 7-day rolling window of notes/todos/sessions (the *why* behind today).
|
|
81
|
+
- The list of available `@<role>` subagents (the *who* of today).
|
|
82
|
+
|
|
83
|
+
Produce markdown that:
|
|
84
|
+
|
|
85
|
+
1. **Opens with a 2-3 sentence preamble** restating the week's arc — what's
|
|
86
|
+
in progress, what shipped yesterday, what's next.
|
|
87
|
+
2. **`## Workflow today`** — a short bulleted list of cross-cutting work the
|
|
88
|
+
*main agent* will run directly (not delegated). Include `/refresh-skills`
|
|
89
|
+
if subagents are stale; surface explicit user todos; surface blockers.
|
|
90
|
+
3. **One `## <role>` section per registered subagent**, each with:
|
|
91
|
+
- **1 must-do** — finishable today, traceable to a card or yesterday's
|
|
92
|
+
summary. Reference the source card id inline (e.g. `(see card-xyz)`).
|
|
93
|
+
- **0–2 stretch goals** — only if there's spare bandwidth.
|
|
94
|
+
- **Blockers** — call out anything the role can't proceed without.
|
|
95
|
+
4. **`## Summary`** — leave empty (`_Subagent results land here after
|
|
96
|
+
`/deepvista run` finishes._`). The `/deepvista run` flow fills it later.
|
|
97
|
+
|
|
98
|
+
Hard constraints:
|
|
99
|
+
|
|
100
|
+
- Total length ≤ 600 words. Brevity beats completeness for a daily plan.
|
|
101
|
+
- Every task must trace to a real card, note, or yesterday's summary —
|
|
102
|
+
never invent work. If a role has nothing genuinely ready, write
|
|
103
|
+
`- _No queued work today — the {role} specialist is free for ad-hoc
|
|
104
|
+
requests._` and move on.
|
|
105
|
+
- Use the section headers (`## Workflow today`, `## <role>`, `## Summary`)
|
|
106
|
+
exactly — the `/deepvista run` slash command parses them by string match.
|
|
107
|
+
|
|
108
|
+
## Step 5 — Save the plan as a DeepVista note
|
|
109
|
+
|
|
110
|
+
Pipe the markdown straight into `deepvista notes create` and tag it so
|
|
111
|
+
tomorrow's run finds it:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
cat <<'PLAN' | deepvista notes create \
|
|
115
|
+
--title "Daily Planning $TODAY" \
|
|
116
|
+
--content-file - \
|
|
117
|
+
--tags "[\"daily-planning\",\"date:$TODAY\",\"source:agent\"]"
|
|
118
|
+
# Daily Planning $TODAY
|
|
119
|
+
|
|
120
|
+
<preamble>
|
|
121
|
+
|
|
122
|
+
## Workflow today
|
|
123
|
+
- …
|
|
124
|
+
|
|
125
|
+
## marketing
|
|
126
|
+
- …
|
|
127
|
+
|
|
128
|
+
## engineering
|
|
129
|
+
- …
|
|
130
|
+
|
|
131
|
+
## Summary
|
|
132
|
+
|
|
133
|
+
_Subagent results land here after `/deepvista run` finishes._
|
|
134
|
+
PLAN
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Confirm with the user before running the save (this is a write command).
|
|
138
|
+
Surface the note URL — `https://app.deepvista.ai/notes/<id>` — in the
|
|
139
|
+
response so they can edit any section before `/deepvista run` dispatches it.
|
|
140
|
+
|
|
141
|
+
## Append a summary after `/deepvista run` finishes
|
|
142
|
+
|
|
143
|
+
The slash command appends each subagent's reply onto today's note. There is
|
|
144
|
+
no dedicated `append-summary` CLI — use `deepvista notes update`:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
deepvista notes get <note-id> # read current description
|
|
148
|
+
# build new_description = current + "\n\n## Summary — <timestamp>\n<consolidated block>\n"
|
|
149
|
+
deepvista notes update <note-id> --content-file - # pipe new_description on stdin
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Quick reference
|
|
153
|
+
|
|
154
|
+
| Command | Reads | Writes |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `card +search "Daily Planning <date>"` | semantic search across cards | — |
|
|
157
|
+
| `notes get <id>` | one note's full body | — |
|
|
158
|
+
| `notes list --limit 20` | recent notes | — |
|
|
159
|
+
| `card list --type todo` | recent todos | — |
|
|
160
|
+
| `notes create --title ... --content-file - --tags ...` | stdin markdown | new note |
|
|
161
|
+
| `notes update <id> --content-file -` | stdin markdown | replaces the note body |
|
|
@@ -54,6 +54,37 @@ deepvista session tick \
|
|
|
54
54
|
deepvista session finalize --session-id "$CLAUDE_SESSION_ID"
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
## Skipping nested sub-agent sessions
|
|
58
|
+
|
|
59
|
+
`session init` short-circuits when `--cwd` matches any pattern in
|
|
60
|
+
`session_skip_cwd_patterns` (a top-level list in
|
|
61
|
+
`~/.config/deepvista/config.json`). This stops the SessionStart hook from
|
|
62
|
+
recording sub-agent sessions whose CWD is not a real working directory — e.g.
|
|
63
|
+
**claude-mem's observer sub-claude** runs with
|
|
64
|
+
`cwd=~/.claude-mem/observer-sessions` and only quotes file paths from the
|
|
65
|
+
*primary* session; recording it pollutes the vistabase with bogus File cards.
|
|
66
|
+
|
|
67
|
+
Defaults skip `*/.claude-mem/observer-sessions[/*]`. Patterns are
|
|
68
|
+
fnmatch-style Unix shell globs (`*` matches across `/`). To override, edit
|
|
69
|
+
`~/.config/deepvista/config.json` by hand — add a top-level
|
|
70
|
+
`session_skip_cwd_patterns` list. An explicit empty list disables skipping.
|
|
71
|
+
|
|
72
|
+
```jsonc
|
|
73
|
+
{
|
|
74
|
+
"default": { "api_url": "https://api.deepvista.ai" },
|
|
75
|
+
"session_skip_cwd_patterns": [
|
|
76
|
+
"*/.claude-mem/observer-sessions",
|
|
77
|
+
"*/.claude-mem/observer-sessions/*",
|
|
78
|
+
"*/scratchpad/*"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
When a session is skipped, `init` emits
|
|
84
|
+
`{"skipped": true, "reason": "cwd matches session_skip_cwd_patterns", ...}`
|
|
85
|
+
and creates no card. Downstream `tick` / `finalize` self-no-op because no
|
|
86
|
+
card was cached (exit 3 "Unknown session", silenced by the hook scripts).
|
|
87
|
+
|
|
57
88
|
## See also
|
|
58
89
|
|
|
59
90
|
- [notes.md](notes.md) — explicit user-authored knowledge.
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
"""Tests for the managed-agent → Claude Code subagent export pipeline (DV-836)."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import time
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
8
|
-
|
|
9
|
-
from deepvista_cli import agent_catalog
|
|
10
|
-
|
|
11
|
-
# ---------------------------------------------------------------------------
|
|
12
|
-
# Fake client
|
|
13
|
-
# ---------------------------------------------------------------------------
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class FakeClient:
|
|
17
|
-
"""In-memory stand-in for ``DeepVistaClient`` (GET only)."""
|
|
18
|
-
|
|
19
|
-
def __init__(self, agents: list[dict[str, Any]]) -> None:
|
|
20
|
-
self._agents = agents
|
|
21
|
-
self.calls: list[tuple[str, dict | None]] = []
|
|
22
|
-
|
|
23
|
-
def get(self, path: str, params: dict | None = None) -> Any:
|
|
24
|
-
self.calls.append((path, params))
|
|
25
|
-
if path == "/agents":
|
|
26
|
-
return {"agents": self._agents}
|
|
27
|
-
raise AssertionError(f"unexpected GET {path}")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def _agent(role: str, *, name: str = "", aid: str = "id-x", updated: str = "2026-01-01") -> dict[str, Any]:
|
|
31
|
-
return {
|
|
32
|
-
"id": aid,
|
|
33
|
-
"name": name or f"{role.title()} Claude",
|
|
34
|
-
"agent_type": "claude-code",
|
|
35
|
-
"agent_role": role,
|
|
36
|
-
"updated_at": updated,
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# ---------------------------------------------------------------------------
|
|
41
|
-
# slug / render
|
|
42
|
-
# ---------------------------------------------------------------------------
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def test_slugify_basic():
|
|
46
|
-
assert agent_catalog.slugify("Marketing") == "marketing"
|
|
47
|
-
assert agent_catalog.slugify("Go To Market!") == "go-to-market"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def test_slugify_fallback_on_empty():
|
|
51
|
-
assert agent_catalog.slugify("你好", fallback="fallback") == "fallback"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def test_file_name_applies_prefix():
|
|
55
|
-
meta = agent_catalog.AgentRoleMeta(role="marketing", agent_name="M", agent_id="a")
|
|
56
|
-
assert agent_catalog.file_name(meta, prefix="dv-") == "dv-marketing.md"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def test_build_agent_markdown_handle_and_marker():
|
|
60
|
-
meta = agent_catalog.AgentRoleMeta(
|
|
61
|
-
role="marketing", agent_name="Marketing Claude", agent_id="abc123", updated_at="2026-05-01"
|
|
62
|
-
)
|
|
63
|
-
md = agent_catalog.build_agent_markdown(meta)
|
|
64
|
-
# Invocation handle is the bare role, so `@marketing` resolves.
|
|
65
|
-
assert "name: marketing" in md
|
|
66
|
-
# Carries our marker + provenance so we can safely reclaim it later.
|
|
67
|
-
assert f"{agent_catalog.AGENT_MARKER}: true" in md
|
|
68
|
-
assert "x-deepvista-role:" in md
|
|
69
|
-
assert "x-deepvista-agent-id: abc123" in md
|
|
70
|
-
# Known role gets its specialist description + preloads the deepvista skill.
|
|
71
|
-
assert "Marketing specialist for DeepVista" in md
|
|
72
|
-
assert "skills: deepvista" in md
|
|
73
|
-
assert "Marketing Claude" in md # backing managed-agent name in the body
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def test_build_agent_markdown_generic_role():
|
|
77
|
-
meta = agent_catalog.AgentRoleMeta(role="legal", agent_name="", agent_id="z")
|
|
78
|
-
md = agent_catalog.build_agent_markdown(meta)
|
|
79
|
-
assert "name: legal" in md
|
|
80
|
-
assert "Legal specialist for DeepVista" in md
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def test_build_agent_markdown_bakes_custom_soul():
|
|
84
|
-
"""A managed agent with config.soul renders that prompt as the body, not the template."""
|
|
85
|
-
meta = agent_catalog.AgentRoleMeta(
|
|
86
|
-
role="marketing",
|
|
87
|
-
agent_name="Marketing",
|
|
88
|
-
agent_id="abc123",
|
|
89
|
-
system_prompt="You are a bespoke marketing brain.\nGround everything in notes.",
|
|
90
|
-
)
|
|
91
|
-
md = agent_catalog.build_agent_markdown(meta)
|
|
92
|
-
# Frontmatter stays templated (routing + preloaded skill + role description).
|
|
93
|
-
assert "name: marketing" in md
|
|
94
|
-
assert "skills: deepvista" in md
|
|
95
|
-
assert "Marketing specialist for DeepVista" in md
|
|
96
|
-
# Body is the custom prompt verbatim; the default template body is bypassed.
|
|
97
|
-
assert "You are a bespoke marketing brain." in md
|
|
98
|
-
assert "Operating procedure" not in md
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def test_metas_from_agents_reads_config_soul():
|
|
102
|
-
"""config.soul flows into AgentRoleMeta.system_prompt for the export to bake in."""
|
|
103
|
-
agents = [
|
|
104
|
-
{
|
|
105
|
-
"id": "m1",
|
|
106
|
-
"name": "Marketing",
|
|
107
|
-
"agent_type": "deepvista-cli",
|
|
108
|
-
"agent_role": "marketing",
|
|
109
|
-
"updated_at": "2026-05-27",
|
|
110
|
-
"config": {"soul": "CUSTOM PROMPT", "machine": "laptop"},
|
|
111
|
-
}
|
|
112
|
-
]
|
|
113
|
-
metas = agent_catalog.metas_from_agents(agents)
|
|
114
|
-
assert len(metas) == 1
|
|
115
|
-
assert metas[0].system_prompt == "CUSTOM PROMPT"
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
# ---------------------------------------------------------------------------
|
|
119
|
-
# grouping
|
|
120
|
-
# ---------------------------------------------------------------------------
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def test_metas_skip_misc_and_empty():
|
|
124
|
-
metas = agent_catalog.metas_from_agents([_agent("misc"), _agent(""), _agent(" "), _agent("sales")])
|
|
125
|
-
assert [m.role for m in metas] == ["sales"]
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def test_metas_group_by_role_keep_freshest_and_count():
|
|
129
|
-
metas = agent_catalog.metas_from_agents(
|
|
130
|
-
[
|
|
131
|
-
_agent("sales", name="Old Sales", aid="a1", updated="2026-01-01"),
|
|
132
|
-
_agent("sales", name="New Sales", aid="a2", updated="2026-05-01"),
|
|
133
|
-
_agent("marketing", aid="b1", updated="2026-02-01"),
|
|
134
|
-
]
|
|
135
|
-
)
|
|
136
|
-
by_role = {m.role: m for m in metas}
|
|
137
|
-
assert set(by_role) == {"sales", "marketing"}
|
|
138
|
-
assert by_role["sales"].agent_id == "a2" # freshest representative
|
|
139
|
-
assert by_role["sales"].agent_name == "New Sales"
|
|
140
|
-
assert by_role["sales"].count == 2
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
# ---------------------------------------------------------------------------
|
|
144
|
-
# curated detection
|
|
145
|
-
# ---------------------------------------------------------------------------
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
def test_curated_names_ignores_our_files(tmp_path: Path):
|
|
149
|
-
# A hand-curated agent (no marker).
|
|
150
|
-
(tmp_path / "marketing.md").write_text("---\nname: marketing\n---\nbody\n", encoding="utf-8")
|
|
151
|
-
# One of ours (carries the marker) — must not count as curated.
|
|
152
|
-
(tmp_path / "dv-sales.md").write_text(
|
|
153
|
-
f"---\nname: sales\n{agent_catalog.AGENT_MARKER}: true\n---\n", encoding="utf-8"
|
|
154
|
-
)
|
|
155
|
-
assert agent_catalog.curated_names(tmp_path, "dv-") == {"marketing"}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def test_plan_skips_curated_role(tmp_path: Path):
|
|
159
|
-
(tmp_path / "marketing.md").write_text("---\nname: marketing\n---\n", encoding="utf-8")
|
|
160
|
-
metas = agent_catalog.metas_from_agents([_agent("marketing"), _agent("sales")])
|
|
161
|
-
plan = agent_catalog.compute_plan(metas, target=tmp_path, prefix="dv-", state={})
|
|
162
|
-
assert plan.skipped_curated == ["marketing"]
|
|
163
|
-
assert [m.slug for m in plan.to_add] == ["sales"]
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
# ---------------------------------------------------------------------------
|
|
167
|
-
# plan + apply + end-to-end
|
|
168
|
-
# ---------------------------------------------------------------------------
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
def test_sync_writes_then_idempotent(tmp_path: Path):
|
|
172
|
-
client = FakeClient([_agent("sales"), _agent("marketing")])
|
|
173
|
-
res = agent_catalog.sync_agent_defs(client, target=tmp_path, force=True, state_path=tmp_path / "state.json")
|
|
174
|
-
assert res["ok"] is True
|
|
175
|
-
assert res["summary"]["added"] == 2
|
|
176
|
-
assert (tmp_path / "dv-sales.md").exists()
|
|
177
|
-
assert (tmp_path / "dv-marketing.md").exists()
|
|
178
|
-
|
|
179
|
-
# Re-run: nothing changes.
|
|
180
|
-
res2 = agent_catalog.sync_agent_defs(client, target=tmp_path, force=True, state_path=tmp_path / "state.json")
|
|
181
|
-
assert res2["summary"] == {"added": 0, "updated": 0, "removed": 0, "unchanged": 2, "skipped_curated": 0}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def test_sync_removes_def_when_role_gone(tmp_path: Path):
|
|
185
|
-
state = tmp_path / "state.json"
|
|
186
|
-
agent_catalog.sync_agent_defs(
|
|
187
|
-
FakeClient([_agent("sales"), _agent("marketing")]), target=tmp_path, force=True, state_path=state
|
|
188
|
-
)
|
|
189
|
-
assert (tmp_path / "dv-marketing.md").exists()
|
|
190
|
-
|
|
191
|
-
# Marketing role disappears server-side → its def is reclaimed.
|
|
192
|
-
res = agent_catalog.sync_agent_defs(FakeClient([_agent("sales")]), target=tmp_path, force=True, state_path=state)
|
|
193
|
-
assert res["summary"]["removed"] == 1
|
|
194
|
-
assert not (tmp_path / "dv-marketing.md").exists()
|
|
195
|
-
assert (tmp_path / "dv-sales.md").exists()
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
def test_sync_never_deletes_unmarked_file(tmp_path: Path):
|
|
199
|
-
state = tmp_path / "state.json"
|
|
200
|
-
agent_catalog.sync_agent_defs(FakeClient([_agent("sales")]), target=tmp_path, force=True, state_path=state)
|
|
201
|
-
# A file sharing our prefix but without the marker must survive a reclaim.
|
|
202
|
-
intruder = tmp_path / "dv-sales.md"
|
|
203
|
-
intruder.write_text("---\nname: sales\n---\nhand edited\n", encoding="utf-8")
|
|
204
|
-
agent_catalog.sync_agent_defs(FakeClient([]), target=tmp_path, force=True, state_path=state)
|
|
205
|
-
assert intruder.exists()
|
|
206
|
-
assert "hand edited" in intruder.read_text(encoding="utf-8")
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
def test_sync_throttled_skips_network(tmp_path: Path):
|
|
210
|
-
state = tmp_path / "state.json"
|
|
211
|
-
agent_catalog.save_state({"last_sync_epoch": int(time.time()), "defs": []}, state)
|
|
212
|
-
client = FakeClient([_agent("sales")])
|
|
213
|
-
res = agent_catalog.sync_agent_defs(client, target=tmp_path, throttle_min=60, state_path=state)
|
|
214
|
-
assert res["skipped"] == "throttled"
|
|
215
|
-
assert client.calls == [] # no network hit
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
def test_dry_run_writes_nothing(tmp_path: Path):
|
|
219
|
-
client = FakeClient([_agent("sales")])
|
|
220
|
-
res = agent_catalog.sync_agent_defs(
|
|
221
|
-
client, target=tmp_path, force=True, dry_run=True, state_path=tmp_path / "s.json"
|
|
222
|
-
)
|
|
223
|
-
assert res["dry_run"] is True
|
|
224
|
-
assert res["plan"]["to_add"] == ["sales"]
|
|
225
|
-
assert not list(tmp_path.glob("dv-*.md"))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/deepvista_cli/resources/workflow_host_runtime.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/scripts/deepvista-session-end.sh
RENAMED
|
File without changes
|
{deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/scripts/deepvista-session-start.sh
RENAMED
|
File without changes
|
{deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/scripts/deepvista-session-turn.sh
RENAMED
|
File without changes
|
{deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/plugins/claude-code/scripts/deepvista-skill-url.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/skill-analyze-notes.md
RENAMED
|
File without changes
|
{deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/skill-create-from-note.md
RENAMED
|
File without changes
|
{deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/skill-import-files.md
RENAMED
|
File without changes
|
{deepvista_cli-0.1.18 → deepvista_cli-0.2.0}/skills/deepvista/reference/skill-research-to-skill.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|