@ijfw/memory-server 1.3.0 → 1.4.0

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.
Files changed (64) hide show
  1. package/fixtures/team/book.json +47 -0
  2. package/fixtures/team/business.json +47 -0
  3. package/fixtures/team/content.json +47 -0
  4. package/fixtures/team/design.json +47 -0
  5. package/fixtures/team/mixed.json +59 -0
  6. package/fixtures/team/research.json +47 -0
  7. package/fixtures/team/software.json +47 -0
  8. package/package.json +1 -9
  9. package/src/active-extension-writer.js +116 -0
  10. package/src/blackboard.js +360 -0
  11. package/src/cli-run.js +91 -0
  12. package/src/codex-agents.js +177 -0
  13. package/src/compute/extract.js +3 -0
  14. package/src/compute/fts5.js +4 -4
  15. package/src/compute/graph-lock.js +0 -2
  16. package/src/compute/migrations/003-tier-semantic.js +3 -3
  17. package/src/compute/runner.js +44 -15
  18. package/src/compute/schema.sql +1 -1
  19. package/src/cross-orchestrator-cli.js +974 -13
  20. package/src/cross-orchestrator.js +9 -1
  21. package/src/dashboard-client.html +144 -1
  22. package/src/dashboard-server.js +75 -2
  23. package/src/design-intelligence.js +721 -0
  24. package/src/dispatch/colon-syntax.js +31 -3
  25. package/src/dispatch/domain-manifest.js +251 -0
  26. package/src/dispatch/extension.js +404 -0
  27. package/src/dispatch/override.js +221 -0
  28. package/src/dispatch-planner.js +1 -0
  29. package/src/dream/runner.mjs +3 -3
  30. package/src/extension-installer.js +1230 -0
  31. package/src/extension-manifest-schema.js +301 -0
  32. package/src/extension-signer.js +740 -0
  33. package/src/gate-result-formatter.js +95 -0
  34. package/src/gate-result-schema.js +274 -0
  35. package/src/gate-result.js +195 -0
  36. package/src/intent-router.js +2 -0
  37. package/src/lib/npm-view.js +1 -0
  38. package/src/memory/fts5.js +3 -3
  39. package/src/memory/migrations/002-tier-semantic.js +2 -2
  40. package/src/memory/staleness.js +1 -1
  41. package/src/memory/tier-promotion.js +6 -6
  42. package/src/memory/tokenize.js +1 -1
  43. package/src/memory-feedback.js +188 -0
  44. package/src/override-manifest-schema.js +146 -0
  45. package/src/override-resolver.js +699 -0
  46. package/src/override-use-registry.js +307 -0
  47. package/src/overrides/presets/academic.md +101 -0
  48. package/src/overrides/presets/book.md +87 -0
  49. package/src/overrides/presets/campaign.md +95 -0
  50. package/src/overrides/presets/screenplay.md +99 -0
  51. package/src/recovery/checkpoint.js +191 -0
  52. package/src/redactor.js +2 -0
  53. package/src/runtime-mediator.js +178 -0
  54. package/src/sandbox.js +17 -3
  55. package/src/server.js +94 -2
  56. package/src/swarm/dispatch-prompt.js +154 -0
  57. package/src/swarm/planner.js +399 -0
  58. package/src/swarm/review.js +136 -0
  59. package/src/swarm/worktree.js +239 -0
  60. package/src/team/generator.js +119 -0
  61. package/src/team/schemas.js +341 -0
  62. package/src/trident/dispatch.js +47 -0
  63. package/src/update-check.js +1 -1
  64. package/src/vectors.js +7 -8
@@ -0,0 +1,47 @@
1
+ {
2
+ "charter": {
3
+ "schema_version": "team-charter/v1",
4
+ "team_name": "book-development-team",
5
+ "project_archetypes": ["book"],
6
+ "roles": [
7
+ {
8
+ "name": "chapter-writer",
9
+ "role_type": "book",
10
+ "model": "sonnet",
11
+ "effort": "medium",
12
+ "phase_scope": ["outline", "draft", "revise"],
13
+ "owns": [{"artifact_type": "chapter", "paths": ["manuscript/chapters/**"]}],
14
+ "reviews": [{"artifact_type": "outline", "criteria": ["structure", "continuity"]}],
15
+ "handoff": {"format": "markdown", "required_sections": ["chapter_summary", "open_threads", "changed_artifacts"]},
16
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": ["manuscript/chapters/**"]}
17
+ },
18
+ {
19
+ "name": "continuity-editor",
20
+ "role_type": "review",
21
+ "model": "sonnet",
22
+ "effort": "medium",
23
+ "phase_scope": ["review", "revise"],
24
+ "owns": [{"artifact_type": "continuity_notes", "paths": ["manuscript/notes/**"]}],
25
+ "reviews": [{"artifact_type": "chapter", "criteria": ["timeline", "voice", "consistency"]}],
26
+ "handoff": {"format": "markdown", "required_sections": ["findings", "continuity_risks"]},
27
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": []}
28
+ }
29
+ ]
30
+ },
31
+ "workflow": {
32
+ "schema_version": "team-workflow/v1",
33
+ "project_archetypes": ["book"],
34
+ "artifacts": [
35
+ {"id": "chapter-draft", "type": "chapter", "paths": ["manuscript/chapters/ch01.md"], "owner": "chapter-writer", "reviewers": ["continuity-editor"], "depends_on": [], "verification": ["continuity review"]},
36
+ {"id": "continuity-notes", "type": "continuity_notes", "paths": ["manuscript/notes/continuity.md"], "owner": "continuity-editor", "reviewers": ["chapter-writer"], "depends_on": ["chapter-draft"], "verification": ["open thread audit"]}
37
+ ],
38
+ "waves": [
39
+ {"id": "w1", "mode": "parallel", "artifact_ids": ["chapter-draft"]},
40
+ {"id": "w2", "mode": "review", "artifact_ids": ["continuity-notes"]}
41
+ ]
42
+ },
43
+ "blackboard": {
44
+ "tasks": [{"id": "task-chapter", "title": "Draft chapter one", "status": "ready", "artifact_ids": ["chapter-draft"], "owner": "chapter-writer"}],
45
+ "claims": [{"id": "claim-chapter", "artifact_id": "chapter-draft", "agent": "chapter-writer", "status": "active", "paths": ["manuscript/chapters/ch01.md"]}]
46
+ }
47
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "charter": {
3
+ "schema_version": "team-charter/v1",
4
+ "team_name": "strategy-operations-team",
5
+ "project_archetypes": ["business"],
6
+ "roles": [
7
+ {
8
+ "name": "strategy-lead",
9
+ "role_type": "business",
10
+ "model": "sonnet",
11
+ "effort": "medium",
12
+ "phase_scope": ["diagnose", "plan", "decide"],
13
+ "owns": [{"artifact_type": "strategy_doc", "paths": ["strategy/**"]}],
14
+ "reviews": [{"artifact_type": "risk_register", "criteria": ["assumptions", "decision-quality"]}],
15
+ "handoff": {"format": "markdown", "required_sections": ["recommendation", "assumptions", "tradeoffs"]},
16
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": []}
17
+ },
18
+ {
19
+ "name": "risk-reviewer",
20
+ "role_type": "review",
21
+ "model": "sonnet",
22
+ "effort": "medium",
23
+ "phase_scope": ["review", "verify"],
24
+ "owns": [{"artifact_type": "risk_register", "paths": ["strategy/risks/**"]}],
25
+ "reviews": [{"artifact_type": "strategy_doc", "criteria": ["feasibility", "downside", "metrics"]}],
26
+ "handoff": {"format": "markdown", "required_sections": ["risks", "mitigations", "decision_points"]},
27
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": []}
28
+ }
29
+ ]
30
+ },
31
+ "workflow": {
32
+ "schema_version": "team-workflow/v1",
33
+ "project_archetypes": ["business"],
34
+ "artifacts": [
35
+ {"id": "operating-plan", "type": "strategy_doc", "paths": ["strategy/operating-plan.md"], "owner": "strategy-lead", "reviewers": ["risk-reviewer"], "depends_on": [], "verification": ["decision review"]},
36
+ {"id": "risk-register", "type": "risk_register", "paths": ["strategy/risks/register.md"], "owner": "risk-reviewer", "reviewers": ["strategy-lead"], "depends_on": ["operating-plan"], "verification": ["assumption audit"]}
37
+ ],
38
+ "waves": [
39
+ {"id": "w1", "mode": "sequential", "artifact_ids": ["operating-plan"]},
40
+ {"id": "w2", "mode": "review", "artifact_ids": ["risk-register"]}
41
+ ]
42
+ },
43
+ "blackboard": {
44
+ "tasks": [{"id": "task-plan", "title": "Draft operating plan", "status": "ready", "artifact_ids": ["operating-plan"], "owner": "strategy-lead"}],
45
+ "claims": [{"id": "claim-plan", "artifact_id": "operating-plan", "agent": "strategy-lead", "status": "active", "paths": ["strategy/operating-plan.md"]}]
46
+ }
47
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "charter": {
3
+ "schema_version": "team-charter/v1",
4
+ "team_name": "content-production-team",
5
+ "project_archetypes": ["content"],
6
+ "roles": [
7
+ {
8
+ "name": "content-strategist",
9
+ "role_type": "content",
10
+ "model": "sonnet",
11
+ "effort": "medium",
12
+ "phase_scope": ["plan", "execute"],
13
+ "owns": [{"artifact_type": "brief", "paths": ["content/briefs/**"]}],
14
+ "reviews": [{"artifact_type": "article", "criteria": ["positioning", "brand-voice"]}],
15
+ "handoff": {"format": "markdown", "required_sections": ["message", "audience", "risks"]},
16
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": []}
17
+ },
18
+ {
19
+ "name": "editor",
20
+ "role_type": "review",
21
+ "model": "sonnet",
22
+ "effort": "medium",
23
+ "phase_scope": ["review", "verify"],
24
+ "owns": [{"artifact_type": "article", "paths": ["content/posts/**"]}],
25
+ "reviews": [{"artifact_type": "brief", "criteria": ["clarity", "seo", "voice"]}],
26
+ "handoff": {"format": "markdown", "required_sections": ["edits", "findings", "publication_status"]},
27
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": ["content/posts/**"]}
28
+ }
29
+ ]
30
+ },
31
+ "workflow": {
32
+ "schema_version": "team-workflow/v1",
33
+ "project_archetypes": ["content"],
34
+ "artifacts": [
35
+ {"id": "campaign-brief", "type": "brief", "paths": ["content/briefs/campaign.md"], "owner": "content-strategist", "reviewers": ["editor"], "depends_on": [], "verification": ["brand voice review"]},
36
+ {"id": "article-draft", "type": "article", "paths": ["content/posts/*.mdx"], "owner": "editor", "reviewers": ["content-strategist"], "depends_on": ["campaign-brief"], "verification": ["seo checklist"]}
37
+ ],
38
+ "waves": [
39
+ {"id": "w1", "mode": "sequential", "artifact_ids": ["campaign-brief"]},
40
+ {"id": "w2", "mode": "review", "artifact_ids": ["article-draft"]}
41
+ ]
42
+ },
43
+ "blackboard": {
44
+ "tasks": [{"id": "task-brief", "title": "Prepare campaign brief", "status": "ready", "artifact_ids": ["campaign-brief"], "owner": "content-strategist"}],
45
+ "claims": [{"id": "claim-brief", "artifact_id": "campaign-brief", "agent": "content-strategist", "status": "active", "paths": ["content/briefs/campaign.md"]}]
46
+ }
47
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "charter": {
3
+ "schema_version": "team-charter/v1",
4
+ "team_name": "design-studio-team",
5
+ "project_archetypes": ["design"],
6
+ "roles": [
7
+ {
8
+ "name": "product-designer",
9
+ "role_type": "design",
10
+ "model": "sonnet",
11
+ "effort": "medium",
12
+ "phase_scope": ["discovery", "shape", "execute"],
13
+ "owns": [{"artifact_type": "screen", "paths": ["design/screens/**"]}],
14
+ "reviews": [{"artifact_type": "prototype", "criteria": ["usability", "accessibility"]}],
15
+ "handoff": {"format": "markdown", "required_sections": ["rationale", "changed_artifacts", "open_questions"]},
16
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": ["design/tokens/**"]}
17
+ },
18
+ {
19
+ "name": "visual-qa",
20
+ "role_type": "review",
21
+ "model": "sonnet",
22
+ "effort": "low",
23
+ "phase_scope": ["review", "verify"],
24
+ "owns": [{"artifact_type": "audit", "paths": ["design/reviews/**"]}],
25
+ "reviews": [{"artifact_type": "screen", "criteria": ["layout", "contrast", "responsive-fit"]}],
26
+ "handoff": {"format": "markdown", "required_sections": ["findings", "severity", "recommendations"]},
27
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": []}
28
+ }
29
+ ]
30
+ },
31
+ "workflow": {
32
+ "schema_version": "team-workflow/v1",
33
+ "project_archetypes": ["design"],
34
+ "artifacts": [
35
+ {"id": "screen-system", "type": "screen", "paths": ["design/screens/**"], "owner": "product-designer", "reviewers": ["visual-qa"], "depends_on": [], "verification": ["visual audit"]},
36
+ {"id": "design-review", "type": "audit", "paths": ["design/reviews/**"], "owner": "visual-qa", "reviewers": ["product-designer"], "depends_on": ["screen-system"], "verification": ["accessibility checklist"]}
37
+ ],
38
+ "waves": [
39
+ {"id": "w1", "mode": "parallel", "artifact_ids": ["screen-system"]},
40
+ {"id": "w2", "mode": "review", "artifact_ids": ["design-review"]}
41
+ ]
42
+ },
43
+ "blackboard": {
44
+ "tasks": [{"id": "task-screens", "title": "Draft core screens", "status": "ready", "artifact_ids": ["screen-system"], "owner": "product-designer"}],
45
+ "claims": [{"id": "claim-screens", "artifact_id": "screen-system", "agent": "product-designer", "status": "active", "paths": ["design/screens/**"]}]
46
+ }
47
+ }
@@ -0,0 +1,59 @@
1
+ {
2
+ "charter": {
3
+ "schema_version": "team-charter/v1",
4
+ "team_name": "mixed-launch-team",
5
+ "project_archetypes": ["software", "design", "content", "mixed"],
6
+ "roles": [
7
+ {
8
+ "name": "app-engineer",
9
+ "role_type": "software",
10
+ "model": "sonnet",
11
+ "effort": "medium",
12
+ "phase_scope": ["execute", "verify"],
13
+ "owns": [{"artifact_type": "app", "paths": ["app/**"]}],
14
+ "reviews": [{"artifact_type": "screen", "criteria": ["integration", "accessibility"]}],
15
+ "handoff": {"format": "markdown", "required_sections": ["changed_artifacts", "tests", "risks"]},
16
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": ["app/routes/**"]}
17
+ },
18
+ {
19
+ "name": "ux-designer",
20
+ "role_type": "design",
21
+ "model": "sonnet",
22
+ "effort": "medium",
23
+ "phase_scope": ["shape", "review"],
24
+ "owns": [{"artifact_type": "screen", "paths": ["design/**"]}],
25
+ "reviews": [{"artifact_type": "app", "criteria": ["usability", "visual-fit"]}],
26
+ "handoff": {"format": "markdown", "required_sections": ["rationale", "screens", "review_notes"]},
27
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": ["design/tokens/**"]}
28
+ },
29
+ {
30
+ "name": "launch-editor",
31
+ "role_type": "content",
32
+ "model": "sonnet",
33
+ "effort": "low",
34
+ "phase_scope": ["execute", "review"],
35
+ "owns": [{"artifact_type": "launch_copy", "paths": ["content/launch/**"]}],
36
+ "reviews": [{"artifact_type": "screen", "criteria": ["message-match", "voice"]}],
37
+ "handoff": {"format": "markdown", "required_sections": ["copy_changes", "voice_notes"]},
38
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": []}
39
+ }
40
+ ]
41
+ },
42
+ "workflow": {
43
+ "schema_version": "team-workflow/v1",
44
+ "project_archetypes": ["software", "design", "content", "mixed"],
45
+ "artifacts": [
46
+ {"id": "app-shell", "type": "app", "paths": ["app/**"], "owner": "app-engineer", "reviewers": ["ux-designer"], "depends_on": [], "verification": ["npm test"]},
47
+ {"id": "product-screens", "type": "screen", "paths": ["design/**"], "owner": "ux-designer", "reviewers": ["app-engineer", "launch-editor"], "depends_on": [], "verification": ["visual audit"]},
48
+ {"id": "launch-copy", "type": "launch_copy", "paths": ["content/launch/**"], "owner": "launch-editor", "reviewers": ["ux-designer"], "depends_on": ["product-screens"], "verification": ["brand voice review"]}
49
+ ],
50
+ "waves": [
51
+ {"id": "w1", "mode": "parallel", "artifact_ids": ["app-shell", "product-screens"]},
52
+ {"id": "w2", "mode": "review", "artifact_ids": ["launch-copy"]}
53
+ ]
54
+ },
55
+ "blackboard": {
56
+ "tasks": [{"id": "task-launch", "title": "Coordinate launch surface", "status": "ready", "artifact_ids": ["app-shell", "product-screens"], "owner": "app-engineer"}],
57
+ "claims": [{"id": "claim-copy", "artifact_id": "launch-copy", "agent": "launch-editor", "status": "active", "paths": ["content/launch/**"]}]
58
+ }
59
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "charter": {
3
+ "schema_version": "team-charter/v1",
4
+ "team_name": "research-synthesis-team",
5
+ "project_archetypes": ["research"],
6
+ "roles": [
7
+ {
8
+ "name": "research-lead",
9
+ "role_type": "research",
10
+ "model": "sonnet",
11
+ "effort": "high",
12
+ "phase_scope": ["question", "collect", "synthesize"],
13
+ "owns": [{"artifact_type": "evidence_table", "paths": ["research/evidence/**"]}],
14
+ "reviews": [{"artifact_type": "synthesis", "criteria": ["method", "source-quality"]}],
15
+ "handoff": {"format": "markdown", "required_sections": ["sources", "findings", "confidence"]},
16
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": []}
17
+ },
18
+ {
19
+ "name": "method-reviewer",
20
+ "role_type": "review",
21
+ "model": "sonnet",
22
+ "effort": "medium",
23
+ "phase_scope": ["review", "verify"],
24
+ "owns": [{"artifact_type": "method_audit", "paths": ["research/reviews/**"]}],
25
+ "reviews": [{"artifact_type": "evidence_table", "criteria": ["bias", "traceability", "coverage"]}],
26
+ "handoff": {"format": "markdown", "required_sections": ["limitations", "risk", "recommendations"]},
27
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": []}
28
+ }
29
+ ]
30
+ },
31
+ "workflow": {
32
+ "schema_version": "team-workflow/v1",
33
+ "project_archetypes": ["research"],
34
+ "artifacts": [
35
+ {"id": "evidence-table", "type": "evidence_table", "paths": ["research/evidence/table.md"], "owner": "research-lead", "reviewers": ["method-reviewer"], "depends_on": [], "verification": ["source audit"]},
36
+ {"id": "method-audit", "type": "method_audit", "paths": ["research/reviews/method.md"], "owner": "method-reviewer", "reviewers": ["research-lead"], "depends_on": ["evidence-table"], "verification": ["methodology review"]}
37
+ ],
38
+ "waves": [
39
+ {"id": "w1", "mode": "parallel", "artifact_ids": ["evidence-table"]},
40
+ {"id": "w2", "mode": "review", "artifact_ids": ["method-audit"]}
41
+ ]
42
+ },
43
+ "blackboard": {
44
+ "tasks": [{"id": "task-evidence", "title": "Build evidence table", "status": "ready", "artifact_ids": ["evidence-table"], "owner": "research-lead"}],
45
+ "claims": [{"id": "claim-evidence", "artifact_id": "evidence-table", "agent": "research-lead", "status": "active", "paths": ["research/evidence/table.md"]}]
46
+ }
47
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "charter": {
3
+ "schema_version": "team-charter/v1",
4
+ "team_name": "software-delivery-team",
5
+ "project_archetypes": ["software"],
6
+ "roles": [
7
+ {
8
+ "name": "implementation-engineer",
9
+ "role_type": "software",
10
+ "model": "sonnet",
11
+ "effort": "medium",
12
+ "phase_scope": ["plan", "execute", "verify"],
13
+ "owns": [{"artifact_type": "module", "paths": ["src/**/*.js"]}],
14
+ "reviews": [{"artifact_type": "test", "criteria": ["coverage", "regression"]}],
15
+ "handoff": {"format": "markdown", "required_sections": ["changed_artifacts", "tests", "risks"]},
16
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": ["src/shared/**"]}
17
+ },
18
+ {
19
+ "name": "test-reviewer",
20
+ "role_type": "qa",
21
+ "model": "sonnet",
22
+ "effort": "low",
23
+ "phase_scope": ["review", "verify"],
24
+ "owns": [{"artifact_type": "test", "paths": ["test/**/*.js"]}],
25
+ "reviews": [{"artifact_type": "module", "criteria": ["behavior", "edge-cases"]}],
26
+ "handoff": {"format": "markdown", "required_sections": ["findings", "verification"]},
27
+ "coordination": {"parallel_safe": true, "claim_required": true, "conflicts_with": []}
28
+ }
29
+ ]
30
+ },
31
+ "workflow": {
32
+ "schema_version": "team-workflow/v1",
33
+ "project_archetypes": ["software"],
34
+ "artifacts": [
35
+ {"id": "runtime-module", "type": "module", "paths": ["src/**/*.js"], "owner": "implementation-engineer", "reviewers": ["test-reviewer"], "depends_on": [], "verification": ["npm test"]},
36
+ {"id": "regression-tests", "type": "test", "paths": ["test/**/*.js"], "owner": "test-reviewer", "reviewers": ["implementation-engineer"], "depends_on": ["runtime-module"], "verification": ["node --test test/**/*.js"]}
37
+ ],
38
+ "waves": [
39
+ {"id": "w1", "mode": "parallel", "artifact_ids": ["runtime-module"]},
40
+ {"id": "w2", "mode": "review", "artifact_ids": ["regression-tests"]}
41
+ ]
42
+ },
43
+ "blackboard": {
44
+ "tasks": [{"id": "task-runtime", "title": "Implement runtime module", "status": "ready", "artifact_ids": ["runtime-module"], "owner": "implementation-engineer", "depends_on": []}],
45
+ "claims": [{"id": "claim-runtime", "artifact_id": "runtime-module", "agent": "implementation-engineer", "status": "active", "paths": ["src/**/*.js"], "claimed_at": "2026-05-15T00:00:00Z"}]
46
+ }
47
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ijfw/memory-server",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Cross-platform persistent memory server for IJFW. 10 MCP tools (memory + admin/update). Works with 13 MCP-using platforms (Claude Code, Codex, Gemini CLI, Cursor, Windsurf, Copilot, Hermes, Wayland, OpenCode, QwenCode, Cline, KimiCode, OpenClaw) plus Aider via rules-only tier.",
5
5
  "author": "Sean Donahoe",
6
6
  "license": "MIT",
@@ -26,14 +26,6 @@
26
26
  "ajv": "^8.12.0",
27
27
  "ajv-formats": "^3.0.1"
28
28
  },
29
- "optionalDependencies": {
30
- "@xenova/transformers": "^2.17.0"
31
- },
32
- "peerDependenciesMeta": {
33
- "@xenova/transformers": {
34
- "optional": true
35
- }
36
- },
37
29
  "files": [
38
30
  "bin/",
39
31
  "src/",
@@ -0,0 +1,116 @@
1
+ /**
2
+ * active-extension-writer.js -- IJFW v1.4.0 W7.1/B2-H-01
3
+ *
4
+ * Writes ~/.ijfw/state/active-extension.json from an extension manifest, and
5
+ * clears it on deactivate. Used by:
6
+ * - `ijfw_run extension:activate <name>` CLI command
7
+ * - installExtension when opts.activate is set
8
+ */
9
+
10
+ import { readFile, writeFile, unlink, mkdir } from 'node:fs/promises';
11
+ import { join, dirname } from 'node:path';
12
+ import { homedir } from 'node:os';
13
+
14
+ const STATE_PATH_REL = ['.ijfw', 'state', 'active-extension.json'];
15
+
16
+ function statePath(home) {
17
+ return join(home || homedir(), ...STATE_PATH_REL);
18
+ }
19
+
20
+ /**
21
+ * Write the active-extension state file from a manifest + scope.
22
+ * Validates required fields before write. Atomic write via tmp+rename.
23
+ *
24
+ * @param {{ name: string, permissions: { reads: string[], writes: string[] } }} manifest
25
+ * @param {'project'|'org'|'user'} scope
26
+ * @param {{ homeDir?: string }} [opts]
27
+ * @returns {Promise<{ ok: boolean, path?: string, error?: string }>}
28
+ */
29
+ export async function writeActiveExtension(manifest, scope, opts = {}) {
30
+ if (!manifest || typeof manifest !== 'object') {
31
+ return { ok: false, error: 'manifest must be an object' };
32
+ }
33
+ if (typeof manifest.name !== 'string' || manifest.name.length === 0) {
34
+ return { ok: false, error: 'manifest.name required' };
35
+ }
36
+ if (!['project', 'org', 'user'].includes(scope)) {
37
+ return { ok: false, error: `invalid scope: ${scope}` };
38
+ }
39
+ if (!manifest.permissions || typeof manifest.permissions !== 'object') {
40
+ return { ok: false, error: 'manifest.permissions required' };
41
+ }
42
+ const reads = Array.isArray(manifest.permissions.reads) ? manifest.permissions.reads : [];
43
+ const writes = Array.isArray(manifest.permissions.writes) ? manifest.permissions.writes : [];
44
+ const out = {
45
+ name: manifest.name,
46
+ scope,
47
+ permissions: { reads, writes },
48
+ activated_at: new Date().toISOString(),
49
+ };
50
+ const home = opts && opts.homeDir ? opts.homeDir : (process.env.HOME || homedir());
51
+ const path = statePath(home);
52
+ await mkdir(dirname(path), { recursive: true });
53
+ const tmp = `${path}.tmp.${process.pid}.${Date.now()}`;
54
+ await writeFile(tmp, JSON.stringify(out, null, 2) + '\n', 'utf8');
55
+ const { rename } = await import('node:fs/promises');
56
+ await rename(tmp, path);
57
+ return { ok: true, path };
58
+ }
59
+
60
+ /**
61
+ * Clear the active-extension state file. Idempotent -- succeeds if file is absent.
62
+ *
63
+ * @param {{ homeDir?: string }} [opts]
64
+ * @returns {Promise<{ ok: boolean, removed: boolean }>}
65
+ */
66
+ export async function clearActiveExtension(opts = {}) {
67
+ const home = opts && opts.homeDir ? opts.homeDir : (process.env.HOME || homedir());
68
+ try {
69
+ await unlink(statePath(home));
70
+ return { ok: true, removed: true };
71
+ } catch (err) {
72
+ if (err && err.code === 'ENOENT') return { ok: true, removed: false };
73
+ return { ok: false, removed: false };
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Read an installed extension's manifest by name. Resolves from project/org/user
79
+ * scope (project first, then org, then user). Used by `extension activate <name>`
80
+ * which doesn't carry the full manifest in args.
81
+ *
82
+ * Scope paths (matching the rest of the codebase):
83
+ * project: <projectRoot>/.ijfw/extensions/<name>/manifest.json
84
+ * org: ~/.ijfw/extensions-org/<name>/manifest.json
85
+ * user: ~/.ijfw/extensions-user/<name>/manifest.json
86
+ *
87
+ * @param {string} name
88
+ * @param {string} [projectRoot]
89
+ * @param {{ homeDir?: string }} [opts]
90
+ * @returns {Promise<{ ok: boolean, manifest?: object, scope?: string, path?: string, error?: string }>}
91
+ */
92
+ export async function findInstalledManifest(name, projectRoot, opts = {}) {
93
+ // eslint-disable-next-line security/detect-unsafe-regex -- anchored, bounded npm name shape; no nested ambiguous repetition
94
+ if (typeof name !== 'string' || !/^(@[a-z0-9-]+\/)?[a-z][a-z0-9-]*$/.test(name)) {
95
+ return { ok: false, error: 'invalid extension name' };
96
+ }
97
+ const home = opts && opts.homeDir ? opts.homeDir : (process.env.HOME || homedir());
98
+ const candidates = [];
99
+ if (projectRoot) {
100
+ candidates.push({ scope: 'project', path: join(projectRoot, '.ijfw', 'extensions', name, 'manifest.json') });
101
+ }
102
+ candidates.push({ scope: 'org', path: join(home, '.ijfw', 'extensions-org', name, 'manifest.json') });
103
+ candidates.push({ scope: 'user', path: join(home, '.ijfw', 'extensions-user', name, 'manifest.json') });
104
+
105
+ for (const c of candidates) {
106
+ try {
107
+ const raw = await readFile(c.path, 'utf8');
108
+ const manifest = JSON.parse(raw);
109
+ return { ok: true, manifest, scope: c.scope, path: c.path };
110
+ } catch (err) {
111
+ if (err && err.code === 'ENOENT') continue;
112
+ if (err instanceof SyntaxError) continue;
113
+ }
114
+ }
115
+ return { ok: false, error: `extension "${name}" not found in project/org/user scope` };
116
+ }