@topy-ai/maggie 0.7.46 → 0.7.47
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-zh-TW.md +12 -4
- package/README.md +16 -6
- package/bin/maggie.js +20 -2
- package/bundled-references/memory-hook.md +18 -5
- package/bundled-skills/maggie-deployment/SKILL.md +6 -0
- package/bundled-skills/maggie-feedback/SKILL.md +3 -1
- package/bundled-skills/maggie-memory/SKILL.md +10 -1
- package/bundled-tools/clis/maggie_deployment.py +47 -0
- package/bundled-tools/clis/maggie_feedback.py +1 -0
- package/bundled-tools/clis/maggie_memory.py +10 -3
- package/bundled-tools/runtime/maggie_memory.py +17 -2
- package/package.json +1 -1
- package/references/memory-hook.md +18 -5
package/README-zh-TW.md
CHANGED
|
@@ -29,9 +29,17 @@ npx @topy-ai/maggie init --scope project --agent all
|
|
|
29
29
|
npx @topy-ai/maggie doctor --scope project --project .
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
目前 release 是 `0.7.
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
目前 release 是 `0.7.47`。本版處理 2026-09-26 五條新 feedback:project-scope
|
|
33
|
+
`maggie init` 會建立本地 memory store;context 預設只讀取本 project scope;memory hook
|
|
34
|
+
在 local Python adapter 不存在時 fallback 到已安裝 CLI;重複 init 保留既有檔案與時間戳;
|
|
35
|
+
`maggie feedback list` 的 `--project` 可放在子命令前或後。Generated VPS runner 現在記錄
|
|
36
|
+
Maggie 版本與 template fingerprint;preflight 會警示過期/無標記 runner,其他路徑可用
|
|
37
|
+
`maggie deployment --check-runner PATH` 檢查。
|
|
38
|
+
|
|
39
|
+
0.7.46 feedback batch 仍包含 release profile capability gate、deployment readiness bounded
|
|
40
|
+
retry、Google account/property target verification、GA/GTM external-write idempotency/readback、
|
|
41
|
+
browser adapter capability preflight、privacy-safe feedback evidence references/relationships,
|
|
42
|
+
以及 root-level unittest import 支援。`browser-audit
|
|
35
43
|
--check-browser` 會在導覽前拒絕 missing/incompatible adapter,包括解析成
|
|
36
44
|
`xdg-open` 的系統 `browse`;feedback graph 不保存 raw payload、logs、credentials
|
|
37
45
|
或本機路徑。另包含 0.7.45 的 Codex/Claude Code 一次性 user-scope 安裝、bundled
|
|
@@ -105,7 +113,7 @@ structure drift 或重複 treatment product 時 fail closed。
|
|
|
105
113
|
deployment、memory、feedback 和 MaggieDash。內容先 draft/review,外部寫入、
|
|
106
114
|
publish 與 production deployment 需要明確確認。
|
|
107
115
|
|
|
108
|
-
目前 source tree 的正式 release 是 `0.7.
|
|
116
|
+
目前 source tree 的正式 release 是 `0.7.47`。本版完成 2026-09-24 booking feedback batch:Calendar grid/free-slot entry 與 host modal focus-restoration evidence。本版新增 Starter marketplace provisioning plan;本版完成 feedback batches 22、23:加入 declarative browser interaction evidence、design markup/style/content-key gate、compiler source anchoring、entity double-escape protection、`update --help` read-only、noindex hypothetical indexability、可過期 baseline acknowledgement、DOM-only link extraction regression,以及 MaggieDash 0.2.10 的 content override、短期 browser session、CDN-safe public-session cache contracts。authentication 和 isolated dev server 仍由 host 負責。`maggie-booking` workflow、傳統 email/password、Stripe、Resend、public booking flow 及既有安全邊界維持不變;provider-bound video generation 與 transactional delivery 仍記錄在 issue #39,前一批延後功能仍在 issue #38,不會直接成為 active memory。0.7.33 記錄並審查一筆更正回饋;確認 0.7.32 已正確處理 batch index,沒有修改已驗證的 shared behavior。0.7.32 加入 feedback 一基索引支援、MaggieDash panel 的 source/freshness/error evidence、Consent Mode 與實際 write/no-op reconciliation、SEO baseline recapture、sitemap origin rebasing、content-diff scope、migration ledger reconciliation、deployment credential preflight、release provenance/runtime preflight、icon release gate、opt-in blog auto-publish gate,以及 least-privilege VPS deployer。也修復 top-level `maggie feedback` dispatcher parity。0.7.31 加入 update 差異摘要、atomic installer copy、subset install manifest merge,以及 inventory drift 時 doctor non-zero。0.7.30 補上 feedback tracking issue closeout gate 與 batch 14/15 audit ledger。0.7.29 修正 npm package landing page 使用英文 README;0.7.28 加入 versioned Gemini model policy、明確 fallback 與 provenance、bundled-first CLI
|
|
109
117
|
dispatch、`maggie --version`、feedback batch review 聚合與重複偵測、marketplace enrichment evidence、booking
|
|
110
118
|
worker/resolver evidence,以及 read-only migration preflight。它也包含 host-owned mobile app surface contract、signed-in
|
|
111
119
|
camera-state QA、直接 Astro route resolution、correlated feedback batch,以及
|
package/README.md
CHANGED
|
@@ -89,10 +89,20 @@ Maggie keeps the existing project foundation and asks for decisions before
|
|
|
89
89
|
shared routes, analytics, or publishing boundaries change. The current
|
|
90
90
|
package ships 20 installable skills and a local-first MaggieDash foundation.
|
|
91
91
|
|
|
92
|
-
The current release is `0.7.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
The current release is `0.7.47`. Project initialization creates a local Maggie
|
|
93
|
+
Memory store, memory contexts default to project scope, and the shared hook
|
|
94
|
+
falls back to the installed CLI if project Python tools are absent. Repeated
|
|
95
|
+
memory initialization preserves existing files and timestamps. Feedback list
|
|
96
|
+
accepts `--project` before or after its subcommand. Generated VPS runners record
|
|
97
|
+
their Maggie version and template fingerprint; deployment preflight warns on
|
|
98
|
+
stale or unmarked runners, with `maggie deployment --check-runner PATH` for
|
|
99
|
+
other locations.
|
|
100
|
+
|
|
101
|
+
The 0.7.46 feedback batch remains included: capability-aware release profiles,
|
|
102
|
+
bounded deployment-readiness retries, verified Google account/target evidence,
|
|
103
|
+
idempotent external-write readback, browser-adapter capability preflight,
|
|
104
|
+
privacy-safe feedback evidence and relationships, and root-level unittest
|
|
105
|
+
import support. `browser-audit --check-browser` rejects missing or incompatible
|
|
96
106
|
adapters before navigation, including a system `browse`/`xdg-open` opener.
|
|
97
107
|
It also includes the 0.7.45 one-time user-scope installation for
|
|
98
108
|
Codex and Claude Code, a bundled workflow runner, and scoped maintenance
|
|
@@ -657,8 +667,8 @@ artifact schemas.
|
|
|
657
667
|
Recommended upgrade sequence for the current release:
|
|
658
668
|
|
|
659
669
|
```bash
|
|
660
|
-
npx @topy-ai/maggie@0.7.
|
|
661
|
-
npx @topy-ai/maggie@0.7.
|
|
670
|
+
npx @topy-ai/maggie@0.7.47 update --scope project --project . --force
|
|
671
|
+
npx @topy-ai/maggie@0.7.47 cleanup --scope project --project .
|
|
662
672
|
```
|
|
663
673
|
|
|
664
674
|
Maintainers should pass npm credentials through the repository helper, never
|
package/bin/maggie.js
CHANGED
|
@@ -19,6 +19,23 @@ const TEMPLATES_ROOT = join(PACKAGE_ROOT, "bundled-templates");
|
|
|
19
19
|
const STATE_DIR = ".maggie";
|
|
20
20
|
const USER_SCOPE = "user";
|
|
21
21
|
const PROJECT_SCOPE = "project";
|
|
22
|
+
const PROJECT_MEMORY_FILES = {
|
|
23
|
+
preferences: "preferences.json",
|
|
24
|
+
conventions: "project-conventions.json",
|
|
25
|
+
lessons: "lessons.json",
|
|
26
|
+
errors: "error-patterns.json",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function initializeProjectMemory(root) {
|
|
30
|
+
const memoryRoot = join(root, STATE_DIR, "memory");
|
|
31
|
+
mkdirSync(memoryRoot, { recursive: true });
|
|
32
|
+
for (const [kind, filename] of Object.entries(PROJECT_MEMORY_FILES)) {
|
|
33
|
+
const path = join(memoryRoot, filename);
|
|
34
|
+
if (!existsSync(path)) {
|
|
35
|
+
writeFileSync(path, `${JSON.stringify({ schemaVersion: 1, kind, updatedAt: new Date().toISOString(), items: [] }, null, 2)}\n`, { flag: "wx" });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
22
39
|
function loadSkillNames() {
|
|
23
40
|
try {
|
|
24
41
|
return JSON.parse(readFileSync(join(SKILLS_ROOT, "catalog.json"), "utf8")).skills.map((skill) => skill.name);
|
|
@@ -488,6 +505,7 @@ function install(args) {
|
|
|
488
505
|
if (existsSync(MARKETPLACE_ROOT)) copyIfMissing(MARKETPLACE_ROOT, join(root, "marketplace"));
|
|
489
506
|
if (existsSync(CONTRACTS_ROOT)) copyIfMissing(CONTRACTS_ROOT, join(root, "contracts"));
|
|
490
507
|
if (existsSync(join(TEMPLATES_ROOT, "maggiedash"))) copyIfMissing(join(TEMPLATES_ROOT, "maggiedash"), join(root, "templates", "maggiedash"));
|
|
508
|
+
initializeProjectMemory(root);
|
|
491
509
|
}
|
|
492
510
|
const manifestRoots = scope === USER_SCOPE ? allUserAgentRoots() : roots;
|
|
493
511
|
const installedUserSkills = scope === USER_SCOPE ? mergedUserSkillNames(manifestRoots) : [];
|
|
@@ -496,7 +514,7 @@ function install(args) {
|
|
|
496
514
|
: { scope, version: PACKAGE_VERSION, agents: roots.map((item) => item.slice(root.length + 1)), skills: mergedSkillNames(root, skills), managedFiles: managedFileDigests(root, roots, skills), installed_at: new Date().toISOString() };
|
|
497
515
|
writeInstallManifest(root, manifest, scope);
|
|
498
516
|
console.log(scope === USER_SCOPE ? `Maggie installed for this user in ${userStateRoot()}` : `Maggie installed in ${root}`);
|
|
499
|
-
console.log(scope === USER_SCOPE ? "Project state remains local; run `maggie doctor --scope user` to verify the user install." : "
|
|
517
|
+
console.log(scope === USER_SCOPE ? "Project state remains local; run `maggie doctor --scope user` to verify the user install." : "Project-scoped Maggie Memory is initialized locally; run `maggie doctor --project .` before using mutating workflows.");
|
|
500
518
|
}
|
|
501
519
|
|
|
502
520
|
function update(args) {
|
|
@@ -591,7 +609,7 @@ function workflowCli(name, args) {
|
|
|
591
609
|
const root = projectRoot(args);
|
|
592
610
|
const script = toolScript(name, root);
|
|
593
611
|
if (!existsSync(script)) throw new Error(`${name} is missing: ${script}`);
|
|
594
|
-
const env =
|
|
612
|
+
const env = { ...process.env, MAGGIE_VERSION: PACKAGE_VERSION };
|
|
595
613
|
const result = spawnSync("python3", [script, ...args], { stdio: "inherit", cwd: root, env });
|
|
596
614
|
if (result.error) throw result.error;
|
|
597
615
|
process.exitCode = result.status ?? 1;
|
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
# Maggie Memory Hook
|
|
2
2
|
|
|
3
|
-
Every Maggie skill invocation
|
|
4
|
-
|
|
3
|
+
Every Maggie skill invocation uses the current project's Maggie Memory by
|
|
4
|
+
default. Project initialization creates `.maggie/memory/` for that repository.
|
|
5
5
|
|
|
6
6
|
## Before work
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Use the project-local adapter when it is present:
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
python3 tools/clis/maggie_memory.py context --project . --skill <current-skill>
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+
If that adapter is absent, use the installed CLI:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
maggie memory context --project . --skill <current-skill>
|
|
18
|
+
```
|
|
19
|
+
|
|
14
20
|
Load the returned active preferences, conventions, lessons, and matching error
|
|
15
21
|
history into the current decision context. Surface material rules and verify
|
|
16
22
|
them against current project evidence. Memory never replaces inspection,
|
|
17
23
|
approval, bootstrap, or production gates.
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
The context response reports `status: "uninitialized"` when Maggie Memory is
|
|
26
|
+
available but the project has not initialized its store. In that case, explain
|
|
27
|
+
that `maggie init --scope project` initializes it; do not call memory
|
|
28
|
+
unavailable. Report unavailable only when neither the project adapter nor the
|
|
29
|
+
installed `maggie` CLI can run. Do not invent or silently reconstruct memory.
|
|
30
|
+
|
|
31
|
+
Context includes only active project-scope items for the current repository.
|
|
32
|
+
Include user/workspace entries only after a separate explicit opt-in with
|
|
33
|
+
`--include-shared`.
|
|
21
34
|
|
|
22
35
|
## After work
|
|
23
36
|
|
|
@@ -219,6 +219,12 @@ files and the application remain separated (`maggie-deploy` versus
|
|
|
219
219
|
Install and review that narrow sudoers policy on the host before executing a
|
|
220
220
|
runner.
|
|
221
221
|
|
|
222
|
+
Generated release runners include their Maggie version and a template
|
|
223
|
+
fingerprint. VPS preflight checks runners under `.maggie/deployment/` and warns
|
|
224
|
+
when they are unmarked or stale; use `--check-runner PATH` for a runner stored
|
|
225
|
+
elsewhere. Regenerate from the reviewed VPS plan, inspect the diff, and replace
|
|
226
|
+
the checked-in runner only after review.
|
|
227
|
+
|
|
222
228
|
VPS plans include a bounded retention policy: keep two immutable releases,
|
|
223
229
|
preserve the `current` target and rollback target, and review prune candidates
|
|
224
230
|
before any operator executes cleanup. Generate a read-only candidate report:
|
|
@@ -30,7 +30,9 @@ maggie feedback --project . collect \
|
|
|
30
30
|
maggie feedback preview .maggie/feedback/<feedback-id>.json --format markdown
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
The draft is written to `.maggie/feedback/`.
|
|
33
|
+
The draft is written to `.maggie/feedback/`. List local drafts with
|
|
34
|
+
`maggie feedback list --project .`; `--project` works before or after the
|
|
35
|
+
subcommand. A draft contains the Maggie version,
|
|
34
36
|
skill, run ID, phase, error fingerprint, expected/actual result, reproduction
|
|
35
37
|
steps, resolution, validation, and metadata-only screenshot references.
|
|
36
38
|
|
|
@@ -29,7 +29,7 @@ truth, content state, or audit log.
|
|
|
29
29
|
|
|
30
30
|
## Workflow
|
|
31
31
|
|
|
32
|
-
1. At the start of a relevant workflow, run `maggie memory context --project . --skill <skill>` and load only matching active items.
|
|
32
|
+
1. At the start of a relevant workflow, run `maggie memory context --project . --skill <skill>` and load only matching active project-scope items. Use `--include-shared` only after explicit opt-in.
|
|
33
33
|
2. Show material preferences, conventions, and prevention lessons before making a decision.
|
|
34
34
|
3. When the user gives a durable preference or confirms a repaired pitfall, save it explicitly as `candidate` or `active` with scope and evidence.
|
|
35
35
|
4. Record every meaningful failure with `record-error`; include a stable fingerprint, resolution, and validation result when fixed.
|
|
@@ -45,6 +45,15 @@ truth, content state, or audit log.
|
|
|
45
45
|
preserving project scope. Expired active items remain inspectable through
|
|
46
46
|
explicit status listing but are excluded from normal context.
|
|
47
47
|
|
|
48
|
+
Project-scope `maggie init` creates the repository's `.maggie/memory/` store.
|
|
49
|
+
Repeated initialization preserves existing memory files without rewriting
|
|
50
|
+
their contents or timestamps. A context response distinguishes a ready store
|
|
51
|
+
from an uninitialized project; the shared memory hook falls back to the
|
|
52
|
+
installed CLI when local Python tools are absent.
|
|
53
|
+
|
|
54
|
+
Project `context`, `search`, and `list` read only project-scope items by
|
|
55
|
+
default. `--include-shared` explicitly opts into user/workspace-scoped entries.
|
|
56
|
+
|
|
48
57
|
```bash
|
|
49
58
|
maggie memory init --project .
|
|
50
59
|
maggie memory context --project . --skill maggie-clone
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import hashlib
|
|
7
|
+
import inspect
|
|
6
8
|
import json
|
|
9
|
+
import os
|
|
7
10
|
import re
|
|
8
11
|
import shlex
|
|
9
12
|
import subprocess
|
|
@@ -352,6 +355,9 @@ def preflight(
|
|
|
352
355
|
result["delegation"] = delegation
|
|
353
356
|
result["vps"] = {"runtime":"Node.js standalone","reverseProxy":"Nginx","processManager":"systemd","releaseLayout":"/var/www/<site>/releases/<release> + current symlink","dns":"Cloudflare DNS or registrar DNS; verify apex and www separately","remoteCommands":["npm ci","npm run build","systemctl restart <service>","systemctl is-active <service>","curl -fsS https://<domain>/robots.txt","curl -fsS https://<domain>/sitemap.xml"],"handoverFields":["host","sshUser","domain","service","release","previousRelease","nginxConfig","migrationVersion","verification","rollback"]}
|
|
354
357
|
result["next_action"] = "review VPS host, SSH user, domain/DNS, systemd service, Nginx config, release path, migrations, and rollback owner before execute"
|
|
358
|
+
runners = sorted(deployment_dir.rglob("release-runner.sh")) if deployment_dir.exists() else []
|
|
359
|
+
result["release_runners"] = [release_runner_status(path) for path in runners[:25]]
|
|
360
|
+
result["warnings"] = [f"generated release runner is stale or unmarked: {item['path']}" for item in result["release_runners"] if item["status"] == "stale"]
|
|
355
361
|
result["blocking_checks"] = ["package_manifest", "build_command", "env_example", "node_runtime_declared", "start_command_or_runtime", "stateless_or_migration_plan", "release_layout_declared", "rollback_command_declared", "release_retention_policy", "data_release_checkpoint", "release_plan_artifact", "systemd_artifact", "nginx_artifact", "deployment_artifacts_secret_free", "rollback_evidence"]
|
|
356
362
|
if require_deployer:
|
|
357
363
|
result["blocking_checks"].append("deployer_delegation")
|
|
@@ -549,15 +555,51 @@ WantedBy=multi-user.target
|
|
|
549
555
|
(output_dir / f"{domain}.conf").write_text(nginx, encoding="utf-8")
|
|
550
556
|
|
|
551
557
|
|
|
558
|
+
def release_runner_generator() -> dict[str, str]:
|
|
559
|
+
version = os.environ.get("MAGGIE_VERSION", "").strip()
|
|
560
|
+
if not version:
|
|
561
|
+
version_file = Path(__file__).resolve().parents[2] / "VERSION"
|
|
562
|
+
try:
|
|
563
|
+
version = version_file.read_text(encoding="utf-8").strip()
|
|
564
|
+
except OSError:
|
|
565
|
+
version = "unknown"
|
|
566
|
+
source = inspect.getsource(release_runner).encode("utf-8")
|
|
567
|
+
return {"version": version, "fingerprint": hashlib.sha256(source).hexdigest()[:16]}
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def release_runner_status(path: Path) -> dict:
|
|
571
|
+
try:
|
|
572
|
+
content = path.read_text(encoding="utf-8", errors="replace")
|
|
573
|
+
except OSError as error:
|
|
574
|
+
return {"path": str(path), "status": "stale", "reason": f"runner cannot be read: {error}", "refresh": "Regenerate from the reviewed VPS plan and inspect the diff."}
|
|
575
|
+
match = re.search(r"^# Maggie release runner: generator=([^ ]+) template=([a-f0-9]{16})$", content, re.MULTILINE)
|
|
576
|
+
current = release_runner_generator()
|
|
577
|
+
if not match:
|
|
578
|
+
return {"path": str(path), "status": "stale", "reason": "runner has no generator version/fingerprint", "currentGenerator": current, "refresh": "Regenerate from the reviewed VPS plan and inspect the diff before replacing this file."}
|
|
579
|
+
generated = {"version": match.group(1), "fingerprint": match.group(2)}
|
|
580
|
+
stale = generated != current
|
|
581
|
+
return {
|
|
582
|
+
"path": str(path),
|
|
583
|
+
"status": "stale" if stale else "current",
|
|
584
|
+
"generatedBy": generated,
|
|
585
|
+
"currentGenerator": current,
|
|
586
|
+
"reason": "runner generator version or template fingerprint has changed" if stale else "runner matches the current generator",
|
|
587
|
+
"refresh": "Regenerate from the reviewed VPS plan and inspect the diff before replacing this file." if stale else "",
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
|
|
552
591
|
def release_runner(plan: dict) -> str:
|
|
553
592
|
"""Render the ordered, reviewable VPS runner for one release."""
|
|
554
593
|
root = plan["release_root"]
|
|
555
594
|
service = plan["service"]
|
|
556
595
|
domain = plan["domain"]
|
|
557
596
|
current = plan["current_release"]
|
|
597
|
+
generator = release_runner_generator()
|
|
558
598
|
return f'''#!/usr/bin/env bash
|
|
559
599
|
set -euo pipefail
|
|
560
600
|
|
|
601
|
+
# Maggie release runner: generator={generator["version"]} template={generator["fingerprint"]}
|
|
602
|
+
|
|
561
603
|
# Migration precedes the symlink flip; later releases carry agent state.
|
|
562
604
|
: "${{RELEASE_ID:?set RELEASE_ID to an immutable release name}}"
|
|
563
605
|
RELEASE_KIND="${{RELEASE_KIND:-upgrade}}"
|
|
@@ -653,6 +695,7 @@ def main() -> int:
|
|
|
653
695
|
parser.add_argument("--deployer-evidence", help="delegation evidence JSON; defaults to .maggie/deployment/deployer-delegation.json")
|
|
654
696
|
parser.add_argument("--plan-dir", help="directory for generated VPS artifacts")
|
|
655
697
|
parser.add_argument("--runner-output", help="write a reviewable ordered VPS release runner")
|
|
698
|
+
parser.add_argument("--check-runner", help="read-only check of a generated runner's Maggie version and template fingerprint")
|
|
656
699
|
parser.add_argument("--retention-plan", action="store_true", help="create a read-only release prune candidate plan")
|
|
657
700
|
parser.add_argument("--current-link", help="current symlink for --retention-plan")
|
|
658
701
|
parser.add_argument("--keep-releases", type=int, default=2)
|
|
@@ -665,6 +708,10 @@ def main() -> int:
|
|
|
665
708
|
parser.add_argument("--has-auth", action="store_true")
|
|
666
709
|
args = parser.parse_args()
|
|
667
710
|
try:
|
|
711
|
+
if args.check_runner:
|
|
712
|
+
report = release_runner_status(Path(args.check_runner).expanduser().resolve())
|
|
713
|
+
print(json.dumps(report, indent=2, ensure_ascii=False))
|
|
714
|
+
return 0
|
|
668
715
|
if args.verify_deployer:
|
|
669
716
|
if not args.deployer_host:
|
|
670
717
|
parser.error("--deployer-host is required with --verify-deployer")
|
|
@@ -575,6 +575,7 @@ def main() -> int:
|
|
|
575
575
|
submit_parser.add_argument("--endpoint", default=DEFAULT_ENDPOINT)
|
|
576
576
|
submit_parser.add_argument("--confirm", action="store_true")
|
|
577
577
|
list_parser = sub.add_parser("list")
|
|
578
|
+
list_parser.add_argument("--project", default=argparse.SUPPRESS)
|
|
578
579
|
args = parser.parse_args()
|
|
579
580
|
try:
|
|
580
581
|
if args.command == "collect": return collect(args)
|
|
@@ -35,12 +35,15 @@ def main() -> int:
|
|
|
35
35
|
listing.add_argument("--status", choices=sorted(memory.STATUSES), default=None)
|
|
36
36
|
listing.add_argument("--skill", default="")
|
|
37
37
|
listing.add_argument("--query", default="")
|
|
38
|
+
listing.add_argument("--include-shared", action="store_true", help="explicitly include user/workspace memory entries")
|
|
38
39
|
search = add_project(sub.add_parser("search"))
|
|
39
40
|
search.add_argument("query")
|
|
40
41
|
search.add_argument("--skill", default="")
|
|
42
|
+
search.add_argument("--include-shared", action="store_true", help="explicitly include user/workspace memory entries")
|
|
41
43
|
context = add_project(sub.add_parser("context"))
|
|
42
44
|
context.add_argument("--skill", default="")
|
|
43
45
|
context.add_argument("--query", default="")
|
|
46
|
+
context.add_argument("--include-shared", action="store_true", help="explicitly include user/workspace memory entries")
|
|
44
47
|
add = add_project(sub.add_parser("add"))
|
|
45
48
|
add.add_argument("kind", choices=("preferences", "conventions", "lessons"))
|
|
46
49
|
add.add_argument("--scope", choices=sorted(memory.SCOPES), required=True)
|
|
@@ -85,17 +88,21 @@ def main() -> int:
|
|
|
85
88
|
output(memory.transition(args.project, args.kind, args.item_id, args.status))
|
|
86
89
|
elif args.command in {"context", "search", "list"}:
|
|
87
90
|
query = args.query if args.command == "search" else getattr(args, "query", "")
|
|
88
|
-
result = memory.relevant(args.project, skill=getattr(args, "skill", ""), query=query, status=getattr(args, "status", None))
|
|
91
|
+
result = memory.relevant(args.project, skill=getattr(args, "skill", ""), query=query, status=getattr(args, "status", None), include_shared=getattr(args, "include_shared", False))
|
|
89
92
|
if args.command == "list" and args.kind:
|
|
90
93
|
result = {args.kind: result[args.kind]}
|
|
91
94
|
if args.command == "list" and args.status:
|
|
92
95
|
result = {kind: [item for item in items if item.get("status") == args.status] for kind, items in result.items()}
|
|
93
|
-
|
|
96
|
+
if args.command == "context":
|
|
97
|
+
ready = memory.initialized(args.project)
|
|
98
|
+
output({"status": "ready" if ready else "uninitialized", "initialized": ready, **result})
|
|
99
|
+
else:
|
|
100
|
+
output(result)
|
|
94
101
|
elif args.command == "export":
|
|
95
102
|
payload = {kind: memory.read(args.project, kind) for kind in memory.KINDS if kind != "errors"}
|
|
96
103
|
if args.public_safe:
|
|
97
104
|
for value in payload.values():
|
|
98
|
-
value["items"] = [item for item in value["items"] if item.get("scope")
|
|
105
|
+
value["items"] = [item for item in value["items"] if item.get("scope") == "project" and item.get("status") == "active"]
|
|
99
106
|
destination = Path(args.output)
|
|
100
107
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
101
108
|
destination.write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
@@ -73,7 +73,16 @@ def write(project: str | Path, kind: str, payload: dict[str, Any]) -> Path:
|
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
def init(project: str | Path) -> dict[str, str]:
|
|
76
|
-
|
|
76
|
+
paths: dict[str, str] = {}
|
|
77
|
+
for kind in KINDS:
|
|
78
|
+
path = memory_path(project, kind)
|
|
79
|
+
if path.exists():
|
|
80
|
+
# Validate existing state, but keep its bytes and mtime unchanged.
|
|
81
|
+
read(project, kind)
|
|
82
|
+
else:
|
|
83
|
+
write(project, kind, empty(kind))
|
|
84
|
+
paths[kind] = str(path)
|
|
85
|
+
return paths
|
|
77
86
|
|
|
78
87
|
|
|
79
88
|
def _text(value: Any, field: str, required: bool = False) -> str:
|
|
@@ -176,11 +185,13 @@ def _active(item: dict[str, Any], project_id: str = "") -> bool:
|
|
|
176
185
|
return not expires or expires > now()
|
|
177
186
|
|
|
178
187
|
|
|
179
|
-
def relevant(project: str | Path, *, skill: str = "", query: str = "", project_id: str = "", status: str | None = None) -> dict[str, list[dict[str, Any]]]:
|
|
188
|
+
def relevant(project: str | Path, *, skill: str = "", query: str = "", project_id: str = "", status: str | None = None, include_shared: bool = False) -> dict[str, list[dict[str, Any]]]:
|
|
180
189
|
terms = set(re.findall(r"[a-z0-9_-]+", f"{skill} {query}".lower()))
|
|
181
190
|
result: dict[str, list[dict[str, Any]]] = {kind: [] for kind in KINDS}
|
|
182
191
|
for kind in KINDS:
|
|
183
192
|
for item in read(project, kind)["items"]:
|
|
193
|
+
if not include_shared and item.get("scope") != "project":
|
|
194
|
+
continue
|
|
184
195
|
if status is not None:
|
|
185
196
|
if item.get("status") != status:
|
|
186
197
|
continue
|
|
@@ -194,6 +205,10 @@ def relevant(project: str | Path, *, skill: str = "", query: str = "", project_i
|
|
|
194
205
|
return result
|
|
195
206
|
|
|
196
207
|
|
|
208
|
+
def initialized(project: str | Path) -> bool:
|
|
209
|
+
return all(memory_path(project, kind).is_file() for kind in KINDS)
|
|
210
|
+
|
|
211
|
+
|
|
197
212
|
def transition(project: str | Path, kind: str, item_id: str, status: str) -> dict[str, Any]:
|
|
198
213
|
if status not in STATUSES:
|
|
199
214
|
raise ValueError(f"invalid status: {status}")
|
package/package.json
CHANGED
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
# Maggie Memory Hook
|
|
2
2
|
|
|
3
|
-
Every Maggie skill invocation
|
|
4
|
-
|
|
3
|
+
Every Maggie skill invocation uses the current project's Maggie Memory by
|
|
4
|
+
default. Project initialization creates `.maggie/memory/` for that repository.
|
|
5
5
|
|
|
6
6
|
## Before work
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Use the project-local adapter when it is present:
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
python3 tools/clis/maggie_memory.py context --project . --skill <current-skill>
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+
If that adapter is absent, use the installed CLI:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
maggie memory context --project . --skill <current-skill>
|
|
18
|
+
```
|
|
19
|
+
|
|
14
20
|
Load the returned active preferences, conventions, lessons, and matching error
|
|
15
21
|
history into the current decision context. Surface material rules and verify
|
|
16
22
|
them against current project evidence. Memory never replaces inspection,
|
|
17
23
|
approval, bootstrap, or production gates.
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
The context response reports `status: "uninitialized"` when Maggie Memory is
|
|
26
|
+
available but the project has not initialized its store. In that case, explain
|
|
27
|
+
that `maggie init --scope project` initializes it; do not call memory
|
|
28
|
+
unavailable. Report unavailable only when neither the project adapter nor the
|
|
29
|
+
installed `maggie` CLI can run. Do not invent or silently reconstruct memory.
|
|
30
|
+
|
|
31
|
+
Context includes only active project-scope items for the current repository.
|
|
32
|
+
Include user/workspace entries only after a separate explicit opt-in with
|
|
33
|
+
`--include-shared`.
|
|
21
34
|
|
|
22
35
|
## After work
|
|
23
36
|
|