aicortexpy 0.2.5__tar.gz → 0.2.7__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.
- {aicortexpy-0.2.5/aicortexpy.egg-info → aicortexpy-0.2.7}/PKG-INFO +1 -1
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/__init__.py +1 -1
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/daemon.py +13 -4
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/execenv.py +12 -2
- aicortexpy-0.2.7/aicortexpy/pinned_workdir.py +127 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/runner.py +4 -1
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/runtime_config.py +33 -20
- {aicortexpy-0.2.5 → aicortexpy-0.2.7/aicortexpy.egg-info}/PKG-INFO +1 -1
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/pyproject.toml +1 -1
- aicortexpy-0.2.5/aicortexpy/pinned_workdir.py +0 -60
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/MANIFEST.in +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/README.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortex/__init__.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortex/__main__.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/__main__.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/acp_client.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/acp_conn.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/acp_errors.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/agent_env.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/app_workdir.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/artifact_ws.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/artifacts.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/attachments.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/auto_commit.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/capabilities.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/chat_acp_pool.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/cli.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/cli_context.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/client.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/codex_config.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/__init__.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/agent_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/attachment_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/auth_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/autopilot_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/config_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/daemon_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/graph_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/issue_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/label_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/meeting_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/memory_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/project_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/repo_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/runtime_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/setup_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/skill_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/squad_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/update_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/version_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/commands/workspace_cmd.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/config.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/agent-memory/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicorkit-atlassian/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicorkit-graph/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicorkit-outlook/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicorkit-wxmsg/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicortex-basics/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicortex-bridge-cdp/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicortex-cli-ops/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicortex-graph/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/graphify/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/question-form/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/test-report-generator/SKILL.md +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/test-report-generator/scripts/generate_cases.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/test-report-generator/scripts/generate_report.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/design_seed.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/diskusage.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/gc.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/handoff_bundle.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/health.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/id_resolve.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/local_skills.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/models.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/oauth_login.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/openclaw_config.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/output.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/permission.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/platform_client.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/platform_info.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/poisoned.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/process.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/procutil.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/prompt.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/repo.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/sandbox.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/security.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/stream.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/terminal.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/tunnel.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/wakeup.py +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy.egg-info/SOURCES.txt +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy.egg-info/dependency_links.txt +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy.egg-info/entry_points.txt +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy.egg-info/requires.txt +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy.egg-info/top_level.txt +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/scripts/aicortex +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/scripts/aicortex.cmd +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/setup.cfg +0 -0
- {aicortexpy-0.2.5 → aicortexpy-0.2.7}/tests/test_app_workdir.py +0 -0
|
@@ -307,11 +307,20 @@ class Daemon:
|
|
|
307
307
|
p = Path(prior)
|
|
308
308
|
roots.append(p.parent if p.name == "workdir" else p)
|
|
309
309
|
if task.project_id.strip():
|
|
310
|
-
from .pinned_workdir import
|
|
310
|
+
from .pinned_workdir import pinned_workdir_path, select_local_path
|
|
311
311
|
|
|
312
|
-
local =
|
|
313
|
-
|
|
314
|
-
|
|
312
|
+
local, _explicit = select_local_path(
|
|
313
|
+
task.project_resources, self.cfg.daemon_id
|
|
314
|
+
)
|
|
315
|
+
local_path = Path(local) if local else None
|
|
316
|
+
exists = False
|
|
317
|
+
if local_path is not None:
|
|
318
|
+
try:
|
|
319
|
+
exists = local_path.is_dir()
|
|
320
|
+
except OSError:
|
|
321
|
+
exists = False
|
|
322
|
+
if exists and local_path is not None:
|
|
323
|
+
roots.append(local_path)
|
|
315
324
|
else:
|
|
316
325
|
roots.append(
|
|
317
326
|
pinned_workdir_path(self.cfg.workspaces_root, ws, task.project_id)
|
|
@@ -13,7 +13,11 @@ from .client import Task
|
|
|
13
13
|
from .codex_config import ensure_codex_multi_agent_config, sanitize_copied_codex_config
|
|
14
14
|
from .openclaw_config import prepare_openclaw_config
|
|
15
15
|
from .app_workdir import app_uses_pinned_workdir, prepare_app_workdir
|
|
16
|
-
from .pinned_workdir import
|
|
16
|
+
from .pinned_workdir import (
|
|
17
|
+
narrow_local_path_resources,
|
|
18
|
+
prepare_pinned_workdir,
|
|
19
|
+
project_uses_pinned_workdir,
|
|
20
|
+
)
|
|
17
21
|
from .prompt import build_prompt
|
|
18
22
|
from .runtime_config import (
|
|
19
23
|
inject_runtime_config,
|
|
@@ -51,9 +55,13 @@ def prepare_environment(
|
|
|
51
55
|
workspaces_root: Path,
|
|
52
56
|
task: Task,
|
|
53
57
|
provider: str,
|
|
58
|
+
*,
|
|
59
|
+
daemon_id: str = "",
|
|
54
60
|
) -> Environment:
|
|
55
61
|
ws = task.workspace_id or "default"
|
|
56
62
|
env_updates: dict[str, str] = {}
|
|
63
|
+
# None leaves project resources unchanged. "" means local_path was not used.
|
|
64
|
+
used_local: str | None = None
|
|
57
65
|
|
|
58
66
|
# Priority 0: App edit/run pinned workdir (same layout as Go daemon).
|
|
59
67
|
if app_uses_pinned_workdir(task):
|
|
@@ -67,7 +75,7 @@ def prepare_environment(
|
|
|
67
75
|
task.app_mode,
|
|
68
76
|
)
|
|
69
77
|
elif project_uses_pinned_workdir(task):
|
|
70
|
-
work_dir = prepare_pinned_workdir(workspaces_root, task)
|
|
78
|
+
work_dir, used_local = prepare_pinned_workdir(workspaces_root, task, daemon_id)
|
|
71
79
|
root_dir = work_dir
|
|
72
80
|
pinned = True
|
|
73
81
|
else:
|
|
@@ -94,6 +102,8 @@ def prepare_environment(
|
|
|
94
102
|
|
|
95
103
|
agent = task.agent or {}
|
|
96
104
|
ctx = task_context_from_task(task)
|
|
105
|
+
if used_local is not None:
|
|
106
|
+
ctx.project_resources = narrow_local_path_resources(ctx.project_resources, used_local)
|
|
97
107
|
runtime_brief = inject_runtime_config(work_dir, provider, ctx)
|
|
98
108
|
write_project_sidecar(work_dir, ctx)
|
|
99
109
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Shared project workdir layout."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from .client import Task
|
|
11
|
+
|
|
12
|
+
log = logging.getLogger("aicortexpy.pinned")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def project_uses_pinned_workdir(task: Task) -> bool:
|
|
16
|
+
return bool(task.project_id.strip())
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _local_path_payload(resources: list[dict[str, Any]]) -> tuple[str, dict[str, str]] | None:
|
|
20
|
+
for r in resources:
|
|
21
|
+
rtype = str(r.get("resource_type") or r.get("type") or "")
|
|
22
|
+
if rtype != "local_path":
|
|
23
|
+
continue
|
|
24
|
+
ref = r.get("resource_ref")
|
|
25
|
+
if ref is None:
|
|
26
|
+
ref = r.get("ref") or {}
|
|
27
|
+
if isinstance(ref, str):
|
|
28
|
+
try:
|
|
29
|
+
ref = json.loads(ref)
|
|
30
|
+
except json.JSONDecodeError:
|
|
31
|
+
return "", {}
|
|
32
|
+
if not isinstance(ref, dict):
|
|
33
|
+
return "", {}
|
|
34
|
+
path = str(ref.get("path") or "").strip()
|
|
35
|
+
by_daemon: dict[str, str] = {}
|
|
36
|
+
raw = ref.get("by_daemon") or {}
|
|
37
|
+
if isinstance(raw, dict):
|
|
38
|
+
for key, value in raw.items():
|
|
39
|
+
daemon_id = str(key).strip()
|
|
40
|
+
directory = str(value).strip() if value is not None else ""
|
|
41
|
+
if daemon_id and directory:
|
|
42
|
+
by_daemon[daemon_id] = directory
|
|
43
|
+
return path, by_daemon
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def select_local_path(resources: list[dict[str, Any]], daemon_id: str) -> tuple[str, bool]:
|
|
48
|
+
"""Return (path, explicit). explicit is true when by_daemon names this daemon.
|
|
49
|
+
|
|
50
|
+
Other daemons use the default path, so each machine does not need a row.
|
|
51
|
+
"""
|
|
52
|
+
parsed = _local_path_payload(resources)
|
|
53
|
+
if parsed is None:
|
|
54
|
+
return "", False
|
|
55
|
+
path, by_daemon = parsed
|
|
56
|
+
daemon_id = (daemon_id or "").strip()
|
|
57
|
+
if daemon_id and by_daemon.get(daemon_id):
|
|
58
|
+
return by_daemon[daemon_id], True
|
|
59
|
+
return path, False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def extract_local_path(resources: list[dict[str, Any]]) -> str:
|
|
63
|
+
path, _explicit = select_local_path(resources, "")
|
|
64
|
+
return path
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def narrow_local_path_resources(resources: list[dict[str, Any]], used_path: str) -> list[dict[str, Any]]:
|
|
68
|
+
"""Keep a single local_path ref of {path: used_path}. Drop it when unused."""
|
|
69
|
+
used_path = (used_path or "").strip()
|
|
70
|
+
out: list[dict[str, Any]] = []
|
|
71
|
+
kept = False
|
|
72
|
+
for r in resources:
|
|
73
|
+
rtype = str(r.get("resource_type") or r.get("type") or "")
|
|
74
|
+
if rtype != "local_path":
|
|
75
|
+
out.append(r)
|
|
76
|
+
continue
|
|
77
|
+
if not used_path or kept:
|
|
78
|
+
continue
|
|
79
|
+
copied = dict(r)
|
|
80
|
+
copied["resource_ref"] = {"path": used_path}
|
|
81
|
+
out.append(copied)
|
|
82
|
+
kept = True
|
|
83
|
+
return out
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def pinned_workdir_path(workspaces_root: Path, workspace_id: str, project_id: str) -> Path:
|
|
87
|
+
return workspaces_root / workspace_id / "projects" / project_id
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def ensure_project_layout(root: Path) -> None:
|
|
91
|
+
"""Create the project workdir itself. Do not add stage subdirectories."""
|
|
92
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _dir_exists(path: Path) -> bool:
|
|
96
|
+
try:
|
|
97
|
+
return path.is_dir()
|
|
98
|
+
except OSError:
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def prepare_pinned_workdir(
|
|
103
|
+
workspaces_root: Path,
|
|
104
|
+
task: Task,
|
|
105
|
+
daemon_id: str = "",
|
|
106
|
+
) -> tuple[Path, str]:
|
|
107
|
+
"""Return (workdir, used_local_path). used_local_path is empty on fallback."""
|
|
108
|
+
local, explicit = select_local_path(task.project_resources, daemon_id)
|
|
109
|
+
if local:
|
|
110
|
+
root = Path(local)
|
|
111
|
+
if _dir_exists(root):
|
|
112
|
+
ensure_project_layout(root)
|
|
113
|
+
log.info("using local_path project workdir %s explicit=%s", root, explicit)
|
|
114
|
+
return root, local
|
|
115
|
+
if explicit or not project_uses_pinned_workdir(task):
|
|
116
|
+
raise RuntimeError(
|
|
117
|
+
f"project local_path: working directory {local!r} is not accessible"
|
|
118
|
+
)
|
|
119
|
+
log.warning(
|
|
120
|
+
"default local_path %s is not on this machine; using the daemon project directory",
|
|
121
|
+
local,
|
|
122
|
+
)
|
|
123
|
+
root = pinned_workdir_path(
|
|
124
|
+
workspaces_root, task.workspace_id or "default", task.project_id
|
|
125
|
+
)
|
|
126
|
+
ensure_project_layout(root)
|
|
127
|
+
return root, ""
|
|
@@ -300,7 +300,10 @@ def run_agent(
|
|
|
300
300
|
) -> RunResult:
|
|
301
301
|
from .security import default_security_policy
|
|
302
302
|
|
|
303
|
-
|
|
303
|
+
daemon_id = cfg.daemon_id if cfg is not None else ""
|
|
304
|
+
env_info: Environment = prepare_environment(
|
|
305
|
+
workspaces_root, task, provider, daemon_id=daemon_id
|
|
306
|
+
)
|
|
304
307
|
work_dir = env_info.work_dir
|
|
305
308
|
policy = security_policy or default_security_policy()
|
|
306
309
|
roots = [str(work_dir)]
|
|
@@ -272,6 +272,7 @@ def _format_project_resource(r: dict[str, Any]) -> str:
|
|
|
272
272
|
if hint:
|
|
273
273
|
out += f" (default branch: `{hint}`)"
|
|
274
274
|
elif rtype == "local_path":
|
|
275
|
+
# by_daemon is resolved before this runs; only this machine's path is shown.
|
|
275
276
|
out = f"**Local path**: `{ref.get('path') or ''}`"
|
|
276
277
|
else:
|
|
277
278
|
out = f"**{rtype}**: `{json.dumps(ref, ensure_ascii=False)}`"
|
|
@@ -280,11 +281,37 @@ def _format_project_resource(r: dict[str, Any]) -> str:
|
|
|
280
281
|
return out
|
|
281
282
|
|
|
282
283
|
|
|
283
|
-
def
|
|
284
|
+
def _project_local_path(ctx: TaskContextForEnv) -> str:
|
|
285
|
+
"""Resolved local_path for this claim (by_daemon already narrowed)."""
|
|
284
286
|
for r in ctx.project_resources:
|
|
285
|
-
if str(r.get("resource_type") or r.get("type") or "")
|
|
286
|
-
|
|
287
|
-
|
|
287
|
+
if str(r.get("resource_type") or r.get("type") or "") != "local_path":
|
|
288
|
+
continue
|
|
289
|
+
ref = r.get("resource_ref") or r.get("ref") or {}
|
|
290
|
+
if isinstance(ref, str):
|
|
291
|
+
try:
|
|
292
|
+
ref = json.loads(ref)
|
|
293
|
+
except json.JSONDecodeError:
|
|
294
|
+
ref = {}
|
|
295
|
+
if not isinstance(ref, dict):
|
|
296
|
+
continue
|
|
297
|
+
path = str(ref.get("path") or "").strip()
|
|
298
|
+
if path:
|
|
299
|
+
return path
|
|
300
|
+
return ""
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _project_has_local_path(ctx: TaskContextForEnv) -> bool:
|
|
304
|
+
return bool(_project_local_path(ctx))
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _project_workdir_line(ctx: TaskContextForEnv) -> str:
|
|
308
|
+
"""Path agents should treat as the project root; local_path overrides logical alias."""
|
|
309
|
+
path = _project_local_path(ctx)
|
|
310
|
+
if path:
|
|
311
|
+
return f"Project working directory: `{path}`\n\n"
|
|
312
|
+
if ctx.project_id:
|
|
313
|
+
return f"Project working directory (logical): `/workspace/{ctx.project_id}/`\n\n"
|
|
314
|
+
return ""
|
|
288
315
|
|
|
289
316
|
|
|
290
317
|
def _has_project_context(ctx: TaskContextForEnv) -> bool:
|
|
@@ -320,17 +347,7 @@ def _project_context_section(ctx: TaskContextForEnv) -> str:
|
|
|
320
347
|
parts = ["## Project Context\n\n"]
|
|
321
348
|
if ctx.project_title:
|
|
322
349
|
parts.append(f"This {scope} belongs to **{ctx.project_title}**.\n\n")
|
|
323
|
-
|
|
324
|
-
parts.append(f"Project working directory (logical): `/workspace/{ctx.project_id}/`\n\n")
|
|
325
|
-
parts.append(
|
|
326
|
-
"Use this shared layout:\n\n"
|
|
327
|
-
"- `docs/` — project materials\n"
|
|
328
|
-
"- `design/` — design HTML and static mockups\n"
|
|
329
|
-
"- `app/` — application / development code\n"
|
|
330
|
-
"- `test/` — tests, fixtures, and reports\n"
|
|
331
|
-
"- `release/` — build artifacts and release notes\n\n"
|
|
332
|
-
"Write only in the directory that matches the current task.\n\n"
|
|
333
|
-
)
|
|
350
|
+
parts.append(_project_workdir_line(ctx))
|
|
334
351
|
if ctx.project_resources:
|
|
335
352
|
parts.append("Project resources (also at `.aicortex/project/resources.json`):\n\n")
|
|
336
353
|
for r in ctx.project_resources:
|
|
@@ -572,11 +589,7 @@ def _design_studio_runtime(provider: str, ctx: TaskContextForEnv) -> str:
|
|
|
572
589
|
parts.append("## Design project context\n\n")
|
|
573
590
|
if ctx.project_title:
|
|
574
591
|
parts.append(f"This design session belongs to **{ctx.project_title}**.\n\n")
|
|
575
|
-
|
|
576
|
-
parts.append(
|
|
577
|
-
f"Project working directory (logical): `/workspace/{ctx.project_id}/`\n"
|
|
578
|
-
"Write design HTML under `design/`.\n\n"
|
|
579
|
-
)
|
|
592
|
+
parts.append(_project_workdir_line(ctx))
|
|
580
593
|
if ctx.project_resources:
|
|
581
594
|
parts.append("Project resources (tokens/branding only):\n\n")
|
|
582
595
|
for r in ctx.project_resources:
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
# PyPI + import + console script entry for full Go CLI replacement.
|
|
7
7
|
name = "aicortexpy"
|
|
8
|
-
version = "0.2.
|
|
8
|
+
version = "0.2.7"
|
|
9
9
|
description = "AICortex Python CLI and local agent runtime daemon (Go CLI compatible)"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
license = { text = "Apache-2.0" }
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
"""Shared project workdir layout."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import json
|
|
6
|
-
import logging
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import Any
|
|
9
|
-
|
|
10
|
-
from .client import Task
|
|
11
|
-
|
|
12
|
-
log = logging.getLogger("aicortexpy.pinned")
|
|
13
|
-
|
|
14
|
-
STAGE_DIRS = ("docs", "design", "app", "test", "release")
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def project_uses_pinned_workdir(task: Task) -> bool:
|
|
18
|
-
return bool(task.project_id.strip())
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def extract_local_path(resources: list[dict[str, Any]]) -> str:
|
|
22
|
-
for r in resources:
|
|
23
|
-
if r.get("resource_type") != "local_path":
|
|
24
|
-
continue
|
|
25
|
-
ref = r.get("resource_ref")
|
|
26
|
-
if isinstance(ref, str):
|
|
27
|
-
try:
|
|
28
|
-
ref = json.loads(ref)
|
|
29
|
-
except json.JSONDecodeError:
|
|
30
|
-
continue
|
|
31
|
-
if isinstance(ref, dict) and ref.get("path"):
|
|
32
|
-
return str(ref["path"])
|
|
33
|
-
return ""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def pinned_workdir_path(workspaces_root: Path, workspace_id: str, project_id: str) -> Path:
|
|
37
|
-
return workspaces_root / workspace_id / "projects" / project_id
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def ensure_project_layout(root: Path) -> None:
|
|
41
|
-
root.mkdir(parents=True, exist_ok=True)
|
|
42
|
-
for name in STAGE_DIRS:
|
|
43
|
-
(root / name).mkdir(parents=True, exist_ok=True)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def prepare_pinned_workdir(
|
|
47
|
-
workspaces_root: Path,
|
|
48
|
-
task: Task,
|
|
49
|
-
) -> Path:
|
|
50
|
-
local = extract_local_path(task.project_resources)
|
|
51
|
-
if local:
|
|
52
|
-
root = Path(local)
|
|
53
|
-
ensure_project_layout(root)
|
|
54
|
-
log.info("using local_path project workdir %s", root)
|
|
55
|
-
return root
|
|
56
|
-
root = pinned_workdir_path(
|
|
57
|
-
workspaces_root, task.workspace_id or "default", task.project_id
|
|
58
|
-
)
|
|
59
|
-
ensure_project_layout(root)
|
|
60
|
-
return root
|
|
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
|
|
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
|
{aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicorkit-atlassian/SKILL.md
RENAMED
|
File without changes
|
{aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicorkit-graph/SKILL.md
RENAMED
|
File without changes
|
{aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicorkit-outlook/SKILL.md
RENAMED
|
File without changes
|
{aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicorkit-wxmsg/SKILL.md
RENAMED
|
File without changes
|
{aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicortex-basics/SKILL.md
RENAMED
|
File without changes
|
{aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicortex-bridge-cdp/SKILL.md
RENAMED
|
File without changes
|
{aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicortex-cli-ops/SKILL.md
RENAMED
|
File without changes
|
{aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/aicortex-graph/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aicortexpy-0.2.5 → aicortexpy-0.2.7}/aicortexpy/data/default_skills/test-report-generator/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
|
|
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
|