@aiwg/cli 0.0.0-bootstrap.0 → 2026.7.18
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/LICENSE +21 -0
- package/README.md +22 -1
- package/bin/aiwg.mjs +349 -0
- package/dist/src/a2a/agent-card.js +145 -0
- package/dist/src/a2a/client.js +411 -0
- package/dist/src/a2a/hitl-cli.js +171 -0
- package/dist/src/a2a/hitl-driver.js +370 -0
- package/dist/src/a2a/hitl.js +181 -0
- package/dist/src/a2a/http.js +193 -0
- package/dist/src/a2a/jcs.js +128 -0
- package/dist/src/a2a/jws.js +187 -0
- package/dist/src/a2a/types.js +24 -0
- package/dist/src/a2a/webhook.js +240 -0
- package/dist/src/activity-log/cli.js +348 -0
- package/dist/src/activity-log/parser.js +81 -0
- package/dist/src/activity-log/types.js +50 -0
- package/dist/src/agents/agent-deployer.js +367 -0
- package/dist/src/agents/agent-packager.js +271 -0
- package/dist/src/agents/agent-validator.js +271 -0
- package/dist/src/agents/grounding/knowledge-base.js +161 -0
- package/dist/src/agents/grounding/types.js +12 -0
- package/dist/src/agents/index.js +10 -0
- package/dist/src/agents/packaged-agent-inventory.js +75 -0
- package/dist/src/agents/types.js +7 -0
- package/dist/src/api/index.d.ts +10 -0
- package/dist/src/api/index.js +10 -0
- package/dist/src/artifacts/address-parser.js +73 -0
- package/dist/src/artifacts/audit/cli.js +183 -0
- package/dist/src/artifacts/audit/drift.js +122 -0
- package/dist/src/artifacts/audit/types.js +12 -0
- package/dist/src/artifacts/backends/graphology-backend.js +160 -0
- package/dist/src/artifacts/backends/json-backend.js +129 -0
- package/dist/src/artifacts/backends/sqlite-backend.js +199 -0
- package/dist/src/artifacts/browser-export.js +622 -0
- package/dist/src/artifacts/capability-resolver.js +94 -0
- package/dist/src/artifacts/checksum-manifest.js +91 -0
- package/dist/src/artifacts/citation-parser.js +162 -0
- package/dist/src/artifacts/cli.js +1486 -0
- package/dist/src/artifacts/corpus-tools/citation-densify.js +311 -0
- package/dist/src/artifacts/corpus-tools/cli.js +393 -0
- package/dist/src/artifacts/corpus-tools/corpus-graph.js +225 -0
- package/dist/src/artifacts/corpus-tools/curator.js +186 -0
- package/dist/src/artifacts/corpus-tools/discovery-log.js +70 -0
- package/dist/src/artifacts/corpus-tools/funder-network.js +141 -0
- package/dist/src/artifacts/corpus-tools/induction-audit.js +233 -0
- package/dist/src/artifacts/corpus-tools/integrity-scan.js +202 -0
- package/dist/src/artifacts/corpus-tools/profile-communities.js +166 -0
- package/dist/src/artifacts/corpus-tools/profile-edges.js +72 -0
- package/dist/src/artifacts/corpus-tools/profile-embed.js +113 -0
- package/dist/src/artifacts/corpus-tools/profile-generate-fm.js +167 -0
- package/dist/src/artifacts/corpus-tools/profile-generate.js +181 -0
- package/dist/src/artifacts/corpus-tools/profile-metrics.js +98 -0
- package/dist/src/artifacts/corpus-tools/profile-status.js +86 -0
- package/dist/src/artifacts/corpus-tools/profile-temporal.js +139 -0
- package/dist/src/artifacts/corpus-tools/radar-init.js +93 -0
- package/dist/src/artifacts/corpus-tools/radar-report.js +118 -0
- package/dist/src/artifacts/corpus-tools/radar-shared.js +189 -0
- package/dist/src/artifacts/corpus-tools/radar-status.js +68 -0
- package/dist/src/artifacts/corpus-tools/sidecar-lint.js +233 -0
- package/dist/src/artifacts/corpus-tools/sidecar-repair.js +277 -0
- package/dist/src/artifacts/corpus-tools/snapshot.js +591 -0
- package/dist/src/artifacts/corpus-tools/source-types.js +164 -0
- package/dist/src/artifacts/corpus-tools/vision-extract.js +243 -0
- package/dist/src/artifacts/corpus-views/build.js +126 -0
- package/dist/src/artifacts/corpus-views/corpus-config.js +124 -0
- package/dist/src/artifacts/corpus-views/ref-parser.js +437 -0
- package/dist/src/artifacts/corpus-views/renderers.js +353 -0
- package/dist/src/artifacts/corpus-views/taxonomies.js +131 -0
- package/dist/src/artifacts/dep-graph.js +173 -0
- package/dist/src/artifacts/discover-facets.js +375 -0
- package/dist/src/artifacts/embedding-index.js +354 -0
- package/dist/src/artifacts/enrichment/cli.js +240 -0
- package/dist/src/artifacts/enrichment/prompt.js +47 -0
- package/dist/src/artifacts/enrichment/store.js +90 -0
- package/dist/src/artifacts/enrichment/types.js +12 -0
- package/dist/src/artifacts/fortemi-core-query-adapter.js +475 -0
- package/dist/src/artifacts/fortemi-core-sync.js +251 -0
- package/dist/src/artifacts/fortemi-shard-export.js +44 -0
- package/dist/src/artifacts/fulltext.js +108 -0
- package/dist/src/artifacts/graph-backend.js +51 -0
- package/dist/src/artifacts/graph-query.js +247 -0
- package/dist/src/artifacts/hybrid-query.js +225 -0
- package/dist/src/artifacts/index-builder.js +1089 -0
- package/dist/src/artifacts/index-reader.js +112 -0
- package/dist/src/artifacts/index-status.js +193 -0
- package/dist/src/artifacts/legacy-index-migration.js +169 -0
- package/dist/src/artifacts/move.js +154 -0
- package/dist/src/artifacts/operational-state.js +239 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +49 -0
- package/dist/src/artifacts/query-engine.js +1955 -0
- package/dist/src/artifacts/source-graph.js +529 -0
- package/dist/src/artifacts/stats.js +169 -0
- package/dist/src/artifacts/types.js +606 -0
- package/dist/src/artifacts/views/cli.js +293 -0
- package/dist/src/artifacts/views/definition.js +170 -0
- package/dist/src/artifacts/views/store.js +149 -0
- package/dist/src/artifacts/views/types.js +8 -0
- package/dist/src/artifacts/watcher.js +255 -0
- package/dist/src/catalog/builtin-models.json +636 -0
- package/dist/src/catalog/cli.js +228 -0
- package/dist/src/catalog/cli.mjs +289 -0
- package/dist/src/catalog/index.js +15 -0
- package/dist/src/catalog/loader.js +217 -0
- package/dist/src/catalog/loader.mjs +251 -0
- package/dist/src/catalog/sources.json +41 -0
- package/dist/src/catalog/types.js +8 -0
- package/dist/src/channel/manager.mjs +504 -0
- package/dist/src/cli/agent-spawn.js +178 -0
- package/dist/src/cli/cli-extension-loader.js +245 -0
- package/dist/src/cli/command-log.js +275 -0
- package/dist/src/cli/config-loader.js +232 -0
- package/dist/src/cli/env.js +82 -0
- package/dist/src/cli/errors.js +150 -0
- package/dist/src/cli/find-package-root.js +40 -0
- package/dist/src/cli/git-hooks.js +250 -0
- package/dist/src/cli/handlers/agentcard.js +206 -0
- package/dist/src/cli/handlers/artifacts.js +76 -0
- package/dist/src/cli/handlers/best-practices-audit.js +280 -0
- package/dist/src/cli/handlers/cockpit.js +137 -0
- package/dist/src/cli/handlers/command-log.js +37 -0
- package/dist/src/cli/handlers/daemon.js +59 -0
- package/dist/src/cli/handlers/diagnose.js +300 -0
- package/dist/src/cli/handlers/execution-mode.js +105 -0
- package/dist/src/cli/handlers/feedback.js +341 -0
- package/dist/src/cli/handlers/help.js +147 -0
- package/dist/src/cli/handlers/index.js +302 -0
- package/dist/src/cli/handlers/init.js +179 -0
- package/dist/src/cli/handlers/install.js +129 -0
- package/dist/src/cli/handlers/issues.js +46 -0
- package/dist/src/cli/handlers/lint.js +42 -0
- package/dist/src/cli/handlers/local-executor.js +315 -0
- package/dist/src/cli/handlers/marketplace.js +168 -0
- package/dist/src/cli/handlers/mc.js +1010 -0
- package/dist/src/cli/handlers/models.js +352 -0
- package/dist/src/cli/handlers/packages.js +155 -0
- package/dist/src/cli/handlers/ralph-launcher.js +717 -0
- package/dist/src/cli/handlers/ralph.js +575 -0
- package/dist/src/cli/handlers/refresh.js +432 -0
- package/dist/src/cli/handlers/regenerate.js +336 -0
- package/dist/src/cli/handlers/repo-access.js +109 -0
- package/dist/src/cli/handlers/run.js +165 -0
- package/dist/src/cli/handlers/runtime-info.js +301 -0
- package/dist/src/cli/handlers/sandbox.js +197 -0
- package/dist/src/cli/handlers/scaffolding.js +149 -0
- package/dist/src/cli/handlers/script-runner.js +121 -0
- package/dist/src/cli/handlers/sdlc-accelerate.js +148 -0
- package/dist/src/cli/handlers/serve.js +1835 -0
- package/dist/src/cli/handlers/session.js +348 -0
- package/dist/src/cli/handlers/setup.js +440 -0
- package/dist/src/cli/handlers/skill-lint.js +367 -0
- package/dist/src/cli/handlers/skill-usage.js +74 -0
- package/dist/src/cli/handlers/steward.js +567 -0
- package/dist/src/cli/handlers/subcommands.js +1702 -0
- package/dist/src/cli/handlers/team.js +337 -0
- package/dist/src/cli/handlers/types.js +11 -0
- package/dist/src/cli/handlers/use.js +2772 -0
- package/dist/src/cli/handlers/utilities.js +703 -0
- package/dist/src/cli/handlers/version.js +134 -0
- package/dist/src/cli/handlers/workspace-context.js +93 -0
- package/dist/src/cli/handlers/workspace.js +126 -0
- package/dist/src/cli/help-generator.js +178 -0
- package/dist/src/cli/hooks/builtin/activity-log-hook.js +126 -0
- package/dist/src/cli/hooks/executor.js +96 -0
- package/dist/src/cli/hooks/index.js +15 -0
- package/dist/src/cli/hooks/registry.js +143 -0
- package/dist/src/cli/hooks/types.js +13 -0
- package/dist/src/cli/log.js +492 -0
- package/dist/src/cli/project-isolation/detect.js +70 -0
- package/dist/src/cli/project-isolation/index.js +7 -0
- package/dist/src/cli/project-isolation/signals.js +17 -0
- package/dist/src/cli/project-isolation/warning.js +102 -0
- package/dist/src/cli/prompt-utils.js +155 -0
- package/dist/src/cli/provider-deployment-plan.js +126 -0
- package/dist/src/cli/provider-resolution.js +119 -0
- package/dist/src/cli/router.js +305 -0
- package/dist/src/cli/scope-resolver.js +392 -0
- package/dist/src/cli/skill-usage.js +616 -0
- package/dist/src/cli/ui.js +224 -0
- package/dist/src/cli/watch-service.js +203 -0
- package/dist/src/cli/workflow-orchestrator.js +478 -0
- package/dist/src/cli/workspace-signals.js +321 -0
- package/dist/src/community/footer.js +14 -0
- package/dist/src/community/links.js +123 -0
- package/dist/src/community/milestones.js +46 -0
- package/dist/src/community/nudge-policy.js +93 -0
- package/dist/src/config/aiwg-config.js +883 -0
- package/dist/src/config/cli.js +703 -0
- package/dist/src/config/gitignore.js +156 -0
- package/dist/src/config/project-artifacts.js +76 -0
- package/dist/src/config/user-config.js +428 -0
- package/dist/src/config/user-registry.js +127 -0
- package/dist/src/config/workspace.js +276 -0
- package/dist/src/contributors/detect.js +39 -0
- package/dist/src/contributors/discover.js +172 -0
- package/dist/src/contributors/heuristic.js +186 -0
- package/dist/src/contributors/index.js +11 -0
- package/dist/src/contributors/types.js +13 -0
- package/dist/src/contributors/validation.js +116 -0
- package/dist/src/data/community.schema.json +43 -0
- package/dist/src/data/community.yaml +16 -0
- package/dist/src/extensions/capability-index.js +270 -0
- package/dist/src/extensions/claude-hooks-installer.js +268 -0
- package/dist/src/extensions/commands/definitions.js +3485 -0
- package/dist/src/extensions/deployment-registration.js +463 -0
- package/dist/src/extensions/index.js +22 -0
- package/dist/src/extensions/loader.js +132 -0
- package/dist/src/extensions/managed-marker.js +100 -0
- package/dist/src/extensions/manifest.js +196 -0
- package/dist/src/extensions/project-local-activity.js +114 -0
- package/dist/src/extensions/project-local-discovery.js +261 -0
- package/dist/src/extensions/project-local-doctor.js +281 -0
- package/dist/src/extensions/project-local-gitignore.js +176 -0
- package/dist/src/extensions/project-local-paths.js +121 -0
- package/dist/src/extensions/project-local-promote.js +218 -0
- package/dist/src/extensions/project-local-remove.js +373 -0
- package/dist/src/extensions/project-local-scaffold.js +252 -0
- package/dist/src/extensions/project-quickref.js +291 -0
- package/dist/src/extensions/registry.js +368 -0
- package/dist/src/extensions/shadow-resolver.js +271 -0
- package/dist/src/extensions/types.js +86 -0
- package/dist/src/extensions/upstream-registry.js +195 -0
- package/dist/src/extensions/validation.js +631 -0
- package/dist/src/features/catalog.js +88 -0
- package/dist/src/features/cli.js +197 -0
- package/dist/src/features/installer.js +57 -0
- package/dist/src/features/paths.js +14 -0
- package/dist/src/features/runtime.js +21 -0
- package/dist/src/features/status.js +134 -0
- package/dist/src/hooks/laziness-detection.js +532 -0
- package/dist/src/intake/git-history-analyzer.js +486 -0
- package/dist/src/intake/index.js +9 -0
- package/dist/src/issues/cli.js +327 -0
- package/dist/src/issues/index.js +5 -0
- package/dist/src/issues/live.js +154 -0
- package/dist/src/issues/local.js +666 -0
- package/dist/src/issues/sync.js +143 -0
- package/dist/src/issues/types.js +2 -0
- package/dist/src/issues/workflows.js +164 -0
- package/dist/src/kb/cli.js +173 -0
- package/dist/src/lint/cli.js +171 -0
- package/dist/src/lint/loader.js +225 -0
- package/dist/src/lint/reporters.js +141 -0
- package/dist/src/lint/runner.js +364 -0
- package/dist/src/lint/types.js +10 -0
- package/dist/src/marketplace/cache.js +110 -0
- package/dist/src/marketplace/identifier.js +63 -0
- package/dist/src/marketplace/registry.js +36 -0
- package/dist/src/marketplace/sources/clawhub.js +53 -0
- package/dist/src/marketplace/sources/git.js +45 -0
- package/dist/src/marketplace/types.js +9 -0
- package/dist/src/mcp/adapters/codex-runtime.js +224 -0
- package/dist/src/mcp/cli.mjs +1197 -0
- package/dist/src/mcp/elicitation.mjs +149 -0
- package/dist/src/mcp/helpers.mjs +287 -0
- package/dist/src/mcp/index.mjs +11 -0
- package/dist/src/mcp/profiles.js +263 -0
- package/dist/src/mcp/profiles.mjs +360 -0
- package/dist/src/mcp/registry.js +376 -0
- package/dist/src/mcp/registry.mjs +387 -0
- package/dist/src/mcp/server.mjs +633 -0
- package/dist/src/mcp/test-simple.mjs +271 -0
- package/dist/src/mcp/tools/command-run.mjs +101 -0
- package/dist/src/mcp/tools/discovery.mjs +291 -0
- package/dist/src/mcp/tools/interaction.mjs +87 -0
- package/dist/src/mcp/tools/orchestration.mjs +320 -0
- package/dist/src/mcp/tools/subsystems.mjs +640 -0
- package/dist/src/memory/cli.js +166 -0
- package/dist/src/memory/project-registry.js +282 -0
- package/dist/src/metrics/artifact-metrics.js +184 -0
- package/dist/src/metrics/context-budget.js +174 -0
- package/dist/src/metrics/token-counter.js +133 -0
- package/dist/src/models/config-loader.js +228 -0
- package/dist/src/models/index.js +22 -0
- package/dist/src/models/model-capabilities.v1.json +120 -0
- package/dist/src/models/model-catalog.v1.json +96 -0
- package/dist/src/models/model-discovery.js +521 -0
- package/dist/src/models/provider-models.js +73 -0
- package/dist/src/models/provider-policy.js +273 -0
- package/dist/src/models/resolver.js +245 -0
- package/dist/src/models/router.js +70 -0
- package/dist/src/models/types.js +8 -0
- package/dist/src/models/wrapper-deployment.js +178 -0
- package/dist/src/models/wrapper-route.js +54 -0
- package/dist/src/ops/cli.js +268 -0
- package/dist/src/ops/registry.js +623 -0
- package/dist/src/packages/adapters/clawhub.js +113 -0
- package/dist/src/packages/adapters/git.js +153 -0
- package/dist/src/packages/adapters/gitea.js +93 -0
- package/dist/src/packages/adapters/github.js +69 -0
- package/dist/src/packages/adapters/local-cache.js +89 -0
- package/dist/src/packages/package-registry.js +143 -0
- package/dist/src/packages/registry.js +238 -0
- package/dist/src/packages/types.js +11 -0
- package/dist/src/plugin/cross-framework-ops.js +475 -0
- package/dist/src/plugin/framework-config-loader.js +235 -0
- package/dist/src/plugin/framework-detector.js +186 -0
- package/dist/src/plugin/framework-isolator.js +341 -0
- package/dist/src/plugin/framework-migration.js +371 -0
- package/dist/src/plugin/metadata-validator.js +906 -0
- package/dist/src/plugin/plugin-installer.js +436 -0
- package/dist/src/plugin/plugin-status.js +369 -0
- package/dist/src/plugin/plugin-uninstaller.js +473 -0
- package/dist/src/plugin/registry-validator.js +344 -0
- package/dist/src/plugin/skill-command-translator.js +415 -0
- package/dist/src/plugin/workspace-creator.js +189 -0
- package/dist/src/plugin/workspace-migrator.js +821 -0
- package/dist/src/policy/authorization.js +390 -0
- package/dist/src/policy/repo-access.js +215 -0
- package/dist/src/provenance/cli.js +16 -0
- package/dist/src/providers/capability-matrix.js +300 -0
- package/dist/src/providers/capability-matrix.yaml +514 -0
- package/dist/src/providers/provider-definitions.js +977 -0
- package/dist/src/providers/provider-definitions.mjs +159 -0
- package/dist/src/providers/provider-inventory.js +169 -0
- package/dist/src/quality/feedback-ab.js +174 -0
- package/dist/src/quality/feedback-collector.js +176 -0
- package/dist/src/quality/feedback-tracker.js +123 -0
- package/dist/src/quality/patterns/adr.json +24 -0
- package/dist/src/quality/patterns/sad.json +24 -0
- package/dist/src/quality/patterns/test-plan.json +24 -0
- package/dist/src/quality/patterns/use-case.json +24 -0
- package/dist/src/quality/scoring.js +157 -0
- package/dist/src/reflections/cli.js +15 -0
- package/dist/src/release/plan.js +139 -0
- package/dist/src/research/cache/manager.js +227 -0
- package/dist/src/research/clients/arxiv.js +156 -0
- package/dist/src/research/clients/base.js +197 -0
- package/dist/src/research/clients/crossref.js +112 -0
- package/dist/src/research/clients/semantic-scholar.js +126 -0
- package/dist/src/research/clients/unpaywall.js +87 -0
- package/dist/src/research/index.js +14 -0
- package/dist/src/research/query-cli.js +336 -0
- package/dist/src/research/services/acquisition.js +313 -0
- package/dist/src/research/services/archival.js +225 -0
- package/dist/src/research/services/citation.js +279 -0
- package/dist/src/research/services/discovery.js +243 -0
- package/dist/src/research/services/documentation.js +269 -0
- package/dist/src/research/services/index.js +14 -0
- package/dist/src/research/services/provenance.js +279 -0
- package/dist/src/research/services/quality.js +370 -0
- package/dist/src/research/services/types.js +7 -0
- package/dist/src/research/storage-cli.js +20 -0
- package/dist/src/research/types.js +47 -0
- package/dist/src/resources/index.d.ts +2 -0
- package/dist/src/resources/index.js +2 -0
- package/dist/src/resources/web-release.d.ts +89 -0
- package/dist/src/resources/web-release.js +886 -0
- package/dist/src/rlm/cache/cli.js +162 -0
- package/dist/src/rlm/cache/hash.js +35 -0
- package/dist/src/rlm/cache/store.js +170 -0
- package/dist/src/rlm/cache/types.js +8 -0
- package/dist/src/rlm/cli.js +650 -0
- package/dist/src/serve/a2a-terminal-observer.js +120 -0
- package/dist/src/serve/agent-router.js +205 -0
- package/dist/src/serve/dispatch-router.js +171 -0
- package/dist/src/serve/executor-registry.js +715 -0
- package/dist/src/serve/mission-conductor.js +177 -0
- package/dist/src/serve/orchestrator-adapter.js +113 -0
- package/dist/src/serve/orchestrator-override.js +94 -0
- package/dist/src/serve/orchestrator-pty.js +133 -0
- package/dist/src/serve/pty-bridge.js +799 -0
- package/dist/src/serve/sandbox-registry.js +832 -0
- package/dist/src/serve/screen-reader.js +228 -0
- package/dist/src/serve/stack-adapters.js +68 -0
- package/dist/src/serve/telemetry.js +143 -0
- package/dist/src/skills/adapters/clawhub.js +250 -0
- package/dist/src/skills/adapters/local.js +335 -0
- package/dist/src/skills/adapters/openclaw.js +316 -0
- package/dist/src/skills/cli.js +334 -0
- package/dist/src/skills/registry.js +117 -0
- package/dist/src/skills/run.js +259 -0
- package/dist/src/skills/runtime.js +94 -0
- package/dist/src/skills/types.js +10 -0
- package/dist/src/smiths/agentsmith/demo.js +116 -0
- package/dist/src/smiths/agentsmith/examples.js +300 -0
- package/dist/src/smiths/agentsmith/generator.js +503 -0
- package/dist/src/smiths/agentsmith/index.js +11 -0
- package/dist/src/smiths/agentsmith/types.js +8 -0
- package/dist/src/smiths/commandsmith/example.js +220 -0
- package/dist/src/smiths/commandsmith/generator.js +288 -0
- package/dist/src/smiths/commandsmith/index.js +41 -0
- package/dist/src/smiths/commandsmith/templates.js +296 -0
- package/dist/src/smiths/commandsmith/types.js +7 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +104 -0
- package/dist/src/smiths/context-pipeline/allowlist.js +195 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +127 -0
- package/dist/src/smiths/context-pipeline/discovery.js +187 -0
- package/dist/src/smiths/context-pipeline/external-links-section.js +49 -0
- package/dist/src/smiths/context-pipeline/finalization.js +147 -0
- package/dist/src/smiths/context-pipeline/generator.js +477 -0
- package/dist/src/smiths/context-pipeline/index.js +30 -0
- package/dist/src/smiths/context-pipeline/legacy-inject.js +103 -0
- package/dist/src/smiths/context-pipeline/managed-hook.js +95 -0
- package/dist/src/smiths/context-pipeline/overflow.js +212 -0
- package/dist/src/smiths/context-pipeline/parallelism-section.js +94 -0
- package/dist/src/smiths/context-pipeline/provider-policy.js +64 -0
- package/dist/src/smiths/context-pipeline/sanitizer.js +93 -0
- package/dist/src/smiths/context-pipeline/types.js +23 -0
- package/dist/src/smiths/context-pipeline/workspace-context.js +988 -0
- package/dist/src/smiths/hook-bridge/codex-translator.js +129 -0
- package/dist/src/smiths/hook-bridge/copilot-translator.js +64 -0
- package/dist/src/smiths/hook-bridge/factory-translator.js +43 -0
- package/dist/src/smiths/hook-bridge/hermes-translator.js +88 -0
- package/dist/src/smiths/hook-bridge/index.js +79 -0
- package/dist/src/smiths/hook-bridge/loader.js +116 -0
- package/dist/src/smiths/hook-bridge/shim.js +89 -0
- package/dist/src/smiths/hook-bridge/types.js +31 -0
- package/dist/src/smiths/index.js +39 -0
- package/dist/src/smiths/mcpsmith/analyzers/cli-analyzer.js +390 -0
- package/dist/src/smiths/mcpsmith/example.js +119 -0
- package/dist/src/smiths/mcpsmith/generator.js +236 -0
- package/dist/src/smiths/mcpsmith/index.js +17 -0
- package/dist/src/smiths/mcpsmith/types.js +10 -0
- package/dist/src/smiths/platform-paths.js +101 -0
- package/dist/src/smiths/skillsmith/codex-sidecar.js +85 -0
- package/dist/src/smiths/skillsmith/collision-detector.js +249 -0
- package/dist/src/smiths/skillsmith/demo.js +83 -0
- package/dist/src/smiths/skillsmith/examples.js +161 -0
- package/dist/src/smiths/skillsmith/generator.js +316 -0
- package/dist/src/smiths/skillsmith/index.js +12 -0
- package/dist/src/smiths/skillsmith/namespace-adapter.js +180 -0
- package/dist/src/smiths/skillsmith/platform-resolver.js +157 -0
- package/dist/src/smiths/skillsmith/types.js +9 -0
- package/dist/src/smiths/toolsmith/index.js +10 -0
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +710 -0
- package/dist/src/smiths/toolsmith/types.js +8 -0
- package/dist/src/storage/backends/fortemi.js +227 -0
- package/dist/src/storage/backends/fs.js +134 -0
- package/dist/src/storage/backends/logseq.js +309 -0
- package/dist/src/storage/backends/obsidian.js +233 -0
- package/dist/src/storage/cli.js +468 -0
- package/dist/src/storage/config.js +272 -0
- package/dist/src/storage/index.js +108 -0
- package/dist/src/storage/subsystem-cli.js +201 -0
- package/dist/src/storage/types.js +33 -0
- package/dist/src/testing/corpus/corpus-builder.js +383 -0
- package/dist/src/testing/corpus/ground-truth-manager.js +460 -0
- package/dist/src/testing/corpus/index.js +10 -0
- package/dist/src/testing/fixtures/index.js +9 -0
- package/dist/src/testing/fixtures/test-data-factory.js +472 -0
- package/dist/src/testing/generators/index.js +11 -0
- package/dist/src/testing/generators/test-case-generator.js +393 -0
- package/dist/src/testing/generators/test-code-generator.js +384 -0
- package/dist/src/testing/generators/use-case-parser.js +325 -0
- package/dist/src/testing/index.js +11 -0
- package/dist/src/tracker/capability-protocol.js +118 -0
- package/dist/src/update/checker.mjs +396 -0
- package/dist/src/update/notifier.mjs +235 -0
- package/dist/src/writing/content-diversifier.js +804 -0
- package/dist/src/writing/example-generator.js +959 -0
- package/dist/src/writing/example-templates.js +99 -0
- package/dist/src/writing/pattern-library.js +486 -0
- package/dist/src/writing/patterns/banned-phrases.json +4413 -0
- package/dist/src/writing/patterns/formulaic-structures.json +1300 -0
- package/dist/src/writing/patterns/generic-adjectives.json +1510 -0
- package/dist/src/writing/patterns/hedging-language.json +2096 -0
- package/dist/src/writing/patterns/transition-words.json +1285 -0
- package/dist/src/writing/patterns/weak-verbs.json +1112 -0
- package/dist/src/writing/prompt-optimizer.js +660 -0
- package/dist/src/writing/prompt-templates.js +583 -0
- package/dist/src/writing/scoring-config-loader.js +168 -0
- package/dist/src/writing/validation-engine.js +595 -0
- package/dist/src/writing/validation-rules.js +380 -0
- package/dist/src/writing/voice-analyzer.js +439 -0
- package/dist/src/writing/voice-calibration.js +661 -0
- package/dist/src/writing/voice-profiles.json +588 -0
- package/package.json +65 -9
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fortemi Storage Adapter
|
|
3
|
+
*
|
|
4
|
+
* Routes storage operations through Fortemi's MCP tool surface. Fortemi
|
|
5
|
+
* is the first-party AIWG semantic-memory project — Rust + PostgreSQL +
|
|
6
|
+
* pgvector + SKOS + W3C PROV — referenced as "Forte" in #934 and
|
|
7
|
+
* confirmed as Fortemi in #961.
|
|
8
|
+
*
|
|
9
|
+
* Tool surface (per `.aiwg/planning/training-framework/phase-4-fortemi-review.md`):
|
|
10
|
+
* capture_knowledge - create note (we use this for first writes)
|
|
11
|
+
* update_note - revise existing note (we use this for re-writes)
|
|
12
|
+
* get_note - retrieve full note (read)
|
|
13
|
+
* list_notes - filter/paginate (list)
|
|
14
|
+
* search - text/semantic/spatial/temporal search (query)
|
|
15
|
+
* manage_collection - organize notes in folders (we use folder=subsystem
|
|
16
|
+
* scope to mirror the StorageAdapter contract)
|
|
17
|
+
*
|
|
18
|
+
* Path semantics:
|
|
19
|
+
* note_id = `${subsystem}:${path}` — the adapter passes the
|
|
20
|
+
* subsystem-relative path; the registry-supplied `subsystem` is
|
|
21
|
+
* prepended to keep different subsystems' notes from colliding.
|
|
22
|
+
*
|
|
23
|
+
* Caveats:
|
|
24
|
+
* - This adapter ships with the parameter shapes documented in the
|
|
25
|
+
* planning doc, but those shapes have NOT yet been validated against
|
|
26
|
+
* a live Fortemi instance. Treat this as alpha. The
|
|
27
|
+
* `McpClientLike.callTool(name, args)` injection point lets tests
|
|
28
|
+
* stub freely; real-world parameter mismatches surface as MCP
|
|
29
|
+
* errors that bubble up to the consumer.
|
|
30
|
+
* - Delete is implemented via `update_note` with `archived: true`
|
|
31
|
+
* because Fortemi's MCP surface does not document a direct delete
|
|
32
|
+
* tool (immutability + versioning is core to the design).
|
|
33
|
+
*
|
|
34
|
+
* @design @.aiwg/architecture/storage-design.md (§5.6)
|
|
35
|
+
* @issue #934
|
|
36
|
+
* @issue #961
|
|
37
|
+
* @issue #972
|
|
38
|
+
*/
|
|
39
|
+
const DEFAULT_MCP_SERVER = 'fortemi';
|
|
40
|
+
export class FortemiAdapter {
|
|
41
|
+
subsystem;
|
|
42
|
+
mcpServer;
|
|
43
|
+
scheme;
|
|
44
|
+
clientFactory;
|
|
45
|
+
client = null;
|
|
46
|
+
constructor(opts) {
|
|
47
|
+
this.subsystem = opts.subsystem;
|
|
48
|
+
this.mcpServer = opts.config.mcpServer ?? DEFAULT_MCP_SERVER;
|
|
49
|
+
this.scheme = opts.config.scheme;
|
|
50
|
+
this.clientFactory = opts.clientFactory ?? createDefaultMcpClient;
|
|
51
|
+
}
|
|
52
|
+
async init() {
|
|
53
|
+
if (this.client)
|
|
54
|
+
return;
|
|
55
|
+
this.client = await this.clientFactory(this.mcpServer);
|
|
56
|
+
}
|
|
57
|
+
async close() {
|
|
58
|
+
if (this.client?.close) {
|
|
59
|
+
await this.client.close();
|
|
60
|
+
}
|
|
61
|
+
this.client = null;
|
|
62
|
+
}
|
|
63
|
+
async getClient() {
|
|
64
|
+
if (!this.client)
|
|
65
|
+
await this.init();
|
|
66
|
+
if (!this.client) {
|
|
67
|
+
throw new Error(`storage(fortemi): MCP client unavailable for server "${this.mcpServer}"`);
|
|
68
|
+
}
|
|
69
|
+
return this.client;
|
|
70
|
+
}
|
|
71
|
+
noteId(path) {
|
|
72
|
+
if (typeof path !== 'string' || path.length === 0) {
|
|
73
|
+
throw new Error('storage(fortemi): path must be a non-empty string');
|
|
74
|
+
}
|
|
75
|
+
if (path.includes('\0')) {
|
|
76
|
+
throw new Error(`storage(fortemi): null bytes not allowed in path "${path}"`);
|
|
77
|
+
}
|
|
78
|
+
return `${this.subsystem}:${path}`;
|
|
79
|
+
}
|
|
80
|
+
async read(path) {
|
|
81
|
+
const id = this.noteId(path);
|
|
82
|
+
const client = await this.getClient();
|
|
83
|
+
const result = (await client.callTool('get_note', { note_id: id }));
|
|
84
|
+
if (!result || result.not_found)
|
|
85
|
+
return null;
|
|
86
|
+
const note = result.note;
|
|
87
|
+
if (!note)
|
|
88
|
+
return null;
|
|
89
|
+
return note.revised_content ?? note.content ?? null;
|
|
90
|
+
}
|
|
91
|
+
async write(path, content, meta) {
|
|
92
|
+
const id = this.noteId(path);
|
|
93
|
+
const client = await this.getClient();
|
|
94
|
+
// Try update first; if not found, capture as new. Two calls in the
|
|
95
|
+
// worst case but idempotent — Fortemi's update_note increments the
|
|
96
|
+
// version rather than overwriting, which matches the Phase-4 design.
|
|
97
|
+
const existing = (await client.callTool('get_note', { note_id: id }));
|
|
98
|
+
if (existing && !existing.not_found && existing.note) {
|
|
99
|
+
await client.callTool('update_note', {
|
|
100
|
+
note_id: id,
|
|
101
|
+
content,
|
|
102
|
+
metadata: this.buildMetadata(meta),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
await client.callTool('capture_knowledge', {
|
|
107
|
+
note_id: id,
|
|
108
|
+
content,
|
|
109
|
+
scheme: this.scheme,
|
|
110
|
+
metadata: this.buildMetadata(meta),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async list(prefix) {
|
|
115
|
+
if (typeof prefix !== 'string') {
|
|
116
|
+
throw new Error('storage(fortemi): list prefix must be a string');
|
|
117
|
+
}
|
|
118
|
+
const client = await this.getClient();
|
|
119
|
+
const subsystemPrefix = `${this.subsystem}:`;
|
|
120
|
+
const fullPrefix = prefix.length === 0 ? subsystemPrefix : `${subsystemPrefix}${prefix}`;
|
|
121
|
+
const result = (await client.callTool('list_notes', {
|
|
122
|
+
id_prefix: fullPrefix,
|
|
123
|
+
scheme: this.scheme,
|
|
124
|
+
}));
|
|
125
|
+
const notes = result?.notes ?? [];
|
|
126
|
+
return notes
|
|
127
|
+
.filter((n) => typeof n.note_id === 'string' && n.note_id.startsWith(subsystemPrefix))
|
|
128
|
+
.map((n) => {
|
|
129
|
+
const entry = {
|
|
130
|
+
path: n.note_id.slice(subsystemPrefix.length),
|
|
131
|
+
externalId: n.note_id,
|
|
132
|
+
};
|
|
133
|
+
if (typeof n.size === 'number')
|
|
134
|
+
entry.size = n.size;
|
|
135
|
+
if (typeof n.updated_at === 'string') {
|
|
136
|
+
const d = new Date(n.updated_at);
|
|
137
|
+
if (!Number.isNaN(d.getTime()))
|
|
138
|
+
entry.modifiedAt = d;
|
|
139
|
+
}
|
|
140
|
+
return entry;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async delete(path) {
|
|
144
|
+
// Fortemi's MCP surface does not document a destructive delete —
|
|
145
|
+
// immutability + versioning is core to the design. We mark the note
|
|
146
|
+
// archived via update_note instead. This matches the storage-design
|
|
147
|
+
// contract (delete is "no-op when missing"; here we just suppress
|
|
148
|
+
// the note from list/read by archiving it).
|
|
149
|
+
const id = this.noteId(path);
|
|
150
|
+
const client = await this.getClient();
|
|
151
|
+
const existing = (await client.callTool('get_note', { note_id: id }));
|
|
152
|
+
if (!existing || existing.not_found || !existing.note)
|
|
153
|
+
return;
|
|
154
|
+
await client.callTool('update_note', {
|
|
155
|
+
note_id: id,
|
|
156
|
+
archived: true,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async query(q) {
|
|
160
|
+
const client = await this.getClient();
|
|
161
|
+
const subsystemPrefix = `${this.subsystem}:`;
|
|
162
|
+
const result = (await client.callTool('search', {
|
|
163
|
+
query: q,
|
|
164
|
+
id_prefix: subsystemPrefix,
|
|
165
|
+
scheme: this.scheme,
|
|
166
|
+
}));
|
|
167
|
+
const results = result?.results ?? [];
|
|
168
|
+
return results
|
|
169
|
+
.filter((r) => typeof r.note_id === 'string' && r.note_id.startsWith(subsystemPrefix))
|
|
170
|
+
.map((r) => ({
|
|
171
|
+
path: r.note_id.slice(subsystemPrefix.length),
|
|
172
|
+
externalId: r.note_id,
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
buildMetadata(meta) {
|
|
176
|
+
const out = {
|
|
177
|
+
subsystem: this.subsystem,
|
|
178
|
+
source: 'aiwg-storage-adapter',
|
|
179
|
+
};
|
|
180
|
+
if (meta?.contentType)
|
|
181
|
+
out['content_type'] = meta.contentType;
|
|
182
|
+
if (meta?.frontmatter)
|
|
183
|
+
out['frontmatter'] = meta.frontmatter;
|
|
184
|
+
if (this.scheme)
|
|
185
|
+
out['scheme'] = this.scheme;
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Default MCP client factory. Resolves the server config from AIWG's
|
|
191
|
+
* McpServerRegistry, spawns the stdio transport, and returns a
|
|
192
|
+
* connected client.
|
|
193
|
+
*
|
|
194
|
+
* Implemented as a lazy import so tests that inject a stub never load
|
|
195
|
+
* the SDK or touch the registry.
|
|
196
|
+
*/
|
|
197
|
+
export const createDefaultMcpClient = async (serverName) => {
|
|
198
|
+
const { McpServerRegistry } = await import('../../mcp/registry.js');
|
|
199
|
+
const registry = new McpServerRegistry();
|
|
200
|
+
const server = await registry.get(serverName);
|
|
201
|
+
if (!server) {
|
|
202
|
+
throw new Error(`storage(fortemi): MCP server "${serverName}" is not registered. ` +
|
|
203
|
+
`Add it via "aiwg mcp add ${serverName} --command <cmd>" before using the fortemi backend.`);
|
|
204
|
+
}
|
|
205
|
+
if (server.type !== 'stdio') {
|
|
206
|
+
throw new Error(`storage(fortemi): only stdio MCP servers are supported (got "${server.type}" for "${serverName}")`);
|
|
207
|
+
}
|
|
208
|
+
// Lazy import the SDK so tests that inject a stub don't pay the cost
|
|
209
|
+
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
|
|
210
|
+
const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');
|
|
211
|
+
const transport = new StdioClientTransport({
|
|
212
|
+
command: server.command ?? '',
|
|
213
|
+
args: server.args ?? [],
|
|
214
|
+
env: server.env,
|
|
215
|
+
});
|
|
216
|
+
const client = new Client({ name: 'aiwg-storage-fortemi-adapter', version: '1.0.0' }, { capabilities: {} });
|
|
217
|
+
await client.connect(transport);
|
|
218
|
+
return {
|
|
219
|
+
async callTool(name, args) {
|
|
220
|
+
return client.callTool({ name, arguments: args });
|
|
221
|
+
},
|
|
222
|
+
async close() {
|
|
223
|
+
await client.close();
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
//# sourceMappingURL=fortemi.js.map
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem Storage Adapter
|
|
3
|
+
*
|
|
4
|
+
* The default backend. Wraps `fs.promises` and honors per-subsystem root
|
|
5
|
+
* overrides from `storage.config`. Output is byte-identical to the
|
|
6
|
+
* pre-abstraction direct-fs calls so consumer migrations stay safe.
|
|
7
|
+
*
|
|
8
|
+
* @design @.aiwg/architecture/storage-design.md (§5.1)
|
|
9
|
+
* @issue #934
|
|
10
|
+
* @issue #956
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync } from 'fs';
|
|
13
|
+
import { appendFile, mkdir, readFile, readdir, rm, stat, writeFile } from 'fs/promises';
|
|
14
|
+
import { dirname, join, relative, resolve, sep } from 'path';
|
|
15
|
+
export class FilesystemAdapter {
|
|
16
|
+
/** Absolute path where this subsystem's content lives. */
|
|
17
|
+
root;
|
|
18
|
+
constructor(root) {
|
|
19
|
+
this.root = resolve(root);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Path traversal guard. Rejects `..`, leading `/`, leading `~`, and
|
|
23
|
+
* backslashes (which on POSIX would be treated as filename chars but
|
|
24
|
+
* are almost always a Windows-vs-POSIX bug). Also rejects empty paths.
|
|
25
|
+
*/
|
|
26
|
+
resolveSafe(relPath) {
|
|
27
|
+
if (typeof relPath !== 'string' || relPath.length === 0) {
|
|
28
|
+
throw new Error('storage(fs): path must be a non-empty string');
|
|
29
|
+
}
|
|
30
|
+
if (relPath.includes('\\')) {
|
|
31
|
+
throw new Error(`storage(fs): backslash not allowed in path "${relPath}"`);
|
|
32
|
+
}
|
|
33
|
+
if (relPath.startsWith('/') || relPath.startsWith('~')) {
|
|
34
|
+
throw new Error(`storage(fs): absolute paths not allowed: "${relPath}"`);
|
|
35
|
+
}
|
|
36
|
+
const segments = relPath.split('/');
|
|
37
|
+
if (segments.some((s) => s === '..')) {
|
|
38
|
+
throw new Error(`storage(fs): ".." traversal not allowed in "${relPath}"`);
|
|
39
|
+
}
|
|
40
|
+
const abs = resolve(this.root, relPath);
|
|
41
|
+
// Final guard: ensure the resolved path is still under root, even if
|
|
42
|
+
// the segment check missed something unusual (e.g. empty segments).
|
|
43
|
+
if (abs !== this.root && !abs.startsWith(this.root + sep)) {
|
|
44
|
+
throw new Error(`storage(fs): resolved path escapes subsystem root: "${relPath}"`);
|
|
45
|
+
}
|
|
46
|
+
return abs;
|
|
47
|
+
}
|
|
48
|
+
async read(path) {
|
|
49
|
+
const abs = this.resolveSafe(path);
|
|
50
|
+
if (!existsSync(abs))
|
|
51
|
+
return null;
|
|
52
|
+
return readFile(abs, 'utf-8');
|
|
53
|
+
}
|
|
54
|
+
async write(path, content, _meta) {
|
|
55
|
+
const abs = this.resolveSafe(path);
|
|
56
|
+
await mkdir(dirname(abs), { recursive: true });
|
|
57
|
+
await writeFile(abs, content, 'utf-8');
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Atomic append. Uses fs.appendFile, which opens with O_APPEND so the
|
|
61
|
+
* kernel guarantees atomicity for writes ≤ PIPE_BUF (4096 bytes on
|
|
62
|
+
* Linux). Concurrent appenders interleave at line granularity rather
|
|
63
|
+
* than racing read-then-write. See #976.
|
|
64
|
+
*/
|
|
65
|
+
async append(path, content) {
|
|
66
|
+
const abs = this.resolveSafe(path);
|
|
67
|
+
await mkdir(dirname(abs), { recursive: true });
|
|
68
|
+
await appendFile(abs, content, 'utf-8');
|
|
69
|
+
}
|
|
70
|
+
async list(prefix) {
|
|
71
|
+
// Empty prefix = list everything under root. A non-empty prefix
|
|
72
|
+
// restricts to entries whose subsystem-relative path starts with it
|
|
73
|
+
// (after path-normalizing the prefix itself).
|
|
74
|
+
if (typeof prefix !== 'string') {
|
|
75
|
+
throw new Error('storage(fs): list prefix must be a string');
|
|
76
|
+
}
|
|
77
|
+
if (prefix.length > 0) {
|
|
78
|
+
// resolveSafe also validates the prefix shape
|
|
79
|
+
this.resolveSafe(prefix);
|
|
80
|
+
}
|
|
81
|
+
if (!existsSync(this.root))
|
|
82
|
+
return [];
|
|
83
|
+
const out = [];
|
|
84
|
+
await walkInto(this.root, this.root, prefix, out);
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
async delete(path) {
|
|
88
|
+
const abs = this.resolveSafe(path);
|
|
89
|
+
if (!existsSync(abs))
|
|
90
|
+
return;
|
|
91
|
+
await rm(abs, { force: true });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function walkInto(root, dir, prefix, out) {
|
|
95
|
+
let entries;
|
|
96
|
+
try {
|
|
97
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
for (const e of entries) {
|
|
103
|
+
const full = join(dir, e.name);
|
|
104
|
+
const rel = relative(root, full).split(sep).join('/');
|
|
105
|
+
if (e.isDirectory()) {
|
|
106
|
+
// Descend if the dir could contain matches
|
|
107
|
+
if (prefix === '' || rel.startsWith(prefix) || prefix.startsWith(rel + '/')) {
|
|
108
|
+
await walkInto(root, full, prefix, out);
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (!e.isFile())
|
|
113
|
+
continue;
|
|
114
|
+
if (prefix !== '' && !rel.startsWith(prefix))
|
|
115
|
+
continue;
|
|
116
|
+
let size;
|
|
117
|
+
let modifiedAt;
|
|
118
|
+
try {
|
|
119
|
+
const s = await stat(full);
|
|
120
|
+
size = s.size;
|
|
121
|
+
modifiedAt = s.mtime;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// ignore stat failures
|
|
125
|
+
}
|
|
126
|
+
const entry = { path: rel };
|
|
127
|
+
if (size !== undefined)
|
|
128
|
+
entry.size = size;
|
|
129
|
+
if (modifiedAt !== undefined)
|
|
130
|
+
entry.modifiedAt = modifiedAt;
|
|
131
|
+
out.push(entry);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=fs.js.map
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logseq Storage Adapter
|
|
3
|
+
*
|
|
4
|
+
* Writes and reads markdown files in a Logseq graph on disk. Like the
|
|
5
|
+
* Obsidian adapter, this is filesystem-shaped — Logseq graphs are
|
|
6
|
+
* directories of `.md` (or `.org`) files plus a `logseq/` config dir
|
|
7
|
+
* we never touch.
|
|
8
|
+
*
|
|
9
|
+
* About the Logseq HTTP API: as of April 2025 the DB-backed Logseq
|
|
10
|
+
* rewrite is still in development; file-backed graphs remain primary.
|
|
11
|
+
* Logseq exposes an HTTP API at `http://127.0.0.1:12315/api` (with a
|
|
12
|
+
* bearer token issued from Settings → Features → Developer mode), but
|
|
13
|
+
* verifying its exact command surface requires hands-on Logseq access.
|
|
14
|
+
*
|
|
15
|
+
* What this adapter does:
|
|
16
|
+
* - Reads / writes / lists / deletes markdown files via fs
|
|
17
|
+
* - Optional HTTP API reachability probe (when `useApi: true`) —
|
|
18
|
+
* emits a one-time stderr warning if `LOGSEQ_API_TOKEN` is unset or
|
|
19
|
+
* the API is unreachable
|
|
20
|
+
* - Refuses paths into the graph's `logseq/` config directory
|
|
21
|
+
* - Transforms `WriteMeta.frontmatter` from YAML into Logseq's
|
|
22
|
+
* page-level `property:: value` syntax
|
|
23
|
+
* - Never writes block IDs (`id::`) — Logseq auto-assigns them
|
|
24
|
+
*
|
|
25
|
+
* Path layout (mirrors Logseq's on-disk conventions):
|
|
26
|
+
* pages/<title>.md regular pages
|
|
27
|
+
* journals/YYYY_MM_DD.md journal entries
|
|
28
|
+
* logseq/* REFUSED (Logseq config)
|
|
29
|
+
*
|
|
30
|
+
* @design @.aiwg/architecture/storage-design.md (§5.3)
|
|
31
|
+
* @issue #934
|
|
32
|
+
* @issue #958
|
|
33
|
+
*/
|
|
34
|
+
import { existsSync } from 'fs';
|
|
35
|
+
import { mkdir, readFile, readdir, rm, stat, writeFile } from 'fs/promises';
|
|
36
|
+
import { dirname, join, relative, resolve, sep } from 'path';
|
|
37
|
+
const LOGSEQ_CONFIG_DIR = 'logseq';
|
|
38
|
+
const DEFAULT_API_URL = 'http://127.0.0.1:12315/api';
|
|
39
|
+
export class LogseqAdapter {
|
|
40
|
+
/** Absolute path to the Logseq graph root. */
|
|
41
|
+
graph;
|
|
42
|
+
apiUrl;
|
|
43
|
+
useApi;
|
|
44
|
+
apiWarned = false;
|
|
45
|
+
constructor(config) {
|
|
46
|
+
this.graph = resolve(expandHome(config.graph));
|
|
47
|
+
this.apiUrl = config.apiUrl ?? DEFAULT_API_URL;
|
|
48
|
+
this.useApi = config.useApi ?? true;
|
|
49
|
+
}
|
|
50
|
+
async init() {
|
|
51
|
+
if (!existsSync(this.graph)) {
|
|
52
|
+
throw new Error(`storage(logseq): graph does not exist: ${this.graph}`);
|
|
53
|
+
}
|
|
54
|
+
if (this.useApi)
|
|
55
|
+
await this.probeApi();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* One-shot API reachability probe. Warns once on missing token or
|
|
59
|
+
* unreachable endpoint. Direct fs writes still work without it.
|
|
60
|
+
*/
|
|
61
|
+
async probeApi() {
|
|
62
|
+
if (this.apiWarned)
|
|
63
|
+
return;
|
|
64
|
+
const token = process.env['LOGSEQ_API_TOKEN'];
|
|
65
|
+
if (!token) {
|
|
66
|
+
this.apiWarned = true;
|
|
67
|
+
process.stderr.write(`storage(logseq): API requested (useApi: true) but LOGSEQ_API_TOKEN is not set.\n` +
|
|
68
|
+
` AIWG will continue with direct file writes against the graph.\n` +
|
|
69
|
+
` Set LOGSEQ_API_TOKEN (Settings → Features → Developer mode) or set "useApi": false to suppress.\n`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
// Lightweight reachability probe — most Logseq API servers respond
|
|
74
|
+
// to a simple POST. We don't synthesize a specific command; just
|
|
75
|
+
// confirm the endpoint is reachable.
|
|
76
|
+
const controller = new AbortController();
|
|
77
|
+
const timer = setTimeout(() => controller.abort(), 2000);
|
|
78
|
+
try {
|
|
79
|
+
await fetch(this.apiUrl, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
82
|
+
body: JSON.stringify({ method: 'logseq.App.getCurrentGraph' }),
|
|
83
|
+
signal: controller.signal,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
clearTimeout(timer);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
this.apiWarned = true;
|
|
92
|
+
process.stderr.write(`storage(logseq): API server at ${this.apiUrl} is not reachable.\n` +
|
|
93
|
+
` AIWG will continue with direct file writes against the graph.\n` +
|
|
94
|
+
` Start Logseq with the API server enabled, or set "useApi": false to suppress.\n`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Validate a subsystem-relative path. Rejects traversal, absolute
|
|
99
|
+
* paths, backslashes, and any path inside the graph's `logseq/` dir.
|
|
100
|
+
*/
|
|
101
|
+
resolveSafe(relPath) {
|
|
102
|
+
if (typeof relPath !== 'string' || relPath.length === 0) {
|
|
103
|
+
throw new Error('storage(logseq): path must be a non-empty string');
|
|
104
|
+
}
|
|
105
|
+
if (relPath.includes('\\')) {
|
|
106
|
+
throw new Error(`storage(logseq): backslash not allowed in path "${relPath}"`);
|
|
107
|
+
}
|
|
108
|
+
if (relPath.startsWith('/') || relPath.startsWith('~')) {
|
|
109
|
+
throw new Error(`storage(logseq): absolute paths not allowed: "${relPath}"`);
|
|
110
|
+
}
|
|
111
|
+
const segments = relPath.split('/');
|
|
112
|
+
if (segments.some((s) => s === '..')) {
|
|
113
|
+
throw new Error(`storage(logseq): ".." traversal not allowed in "${relPath}"`);
|
|
114
|
+
}
|
|
115
|
+
const abs = resolve(this.graph, relPath);
|
|
116
|
+
if (abs !== this.graph && !abs.startsWith(this.graph + sep)) {
|
|
117
|
+
throw new Error(`storage(logseq): resolved path escapes graph root: "${relPath}"`);
|
|
118
|
+
}
|
|
119
|
+
const relFromGraph = relative(this.graph, abs);
|
|
120
|
+
if (relFromGraph === LOGSEQ_CONFIG_DIR ||
|
|
121
|
+
relFromGraph.startsWith(LOGSEQ_CONFIG_DIR + sep)) {
|
|
122
|
+
throw new Error(`storage(logseq): refusing to operate on logseq/ graph config (${relPath})`);
|
|
123
|
+
}
|
|
124
|
+
return abs;
|
|
125
|
+
}
|
|
126
|
+
async read(path) {
|
|
127
|
+
const abs = this.resolveSafe(path);
|
|
128
|
+
if (!existsSync(abs))
|
|
129
|
+
return null;
|
|
130
|
+
return readFile(abs, 'utf-8');
|
|
131
|
+
}
|
|
132
|
+
async write(path, content, meta) {
|
|
133
|
+
const abs = this.resolveSafe(path);
|
|
134
|
+
await mkdir(dirname(abs), { recursive: true });
|
|
135
|
+
let body = content;
|
|
136
|
+
// Strip YAML frontmatter from the content (if any) — Logseq doesn't
|
|
137
|
+
// recognize it. Move what we can to page-level properties.
|
|
138
|
+
const yaml = extractYamlFrontmatter(body);
|
|
139
|
+
if (yaml) {
|
|
140
|
+
body = yaml.body;
|
|
141
|
+
}
|
|
142
|
+
// Merge supplied frontmatter + extracted YAML into Logseq page-properties
|
|
143
|
+
const merged = { ...(yaml?.fields ?? {}), ...(meta?.frontmatter ?? {}) };
|
|
144
|
+
if (Object.keys(merged).length > 0) {
|
|
145
|
+
body = renderPageProperties(merged) + body;
|
|
146
|
+
}
|
|
147
|
+
// Defensive: refuse to write `id::` block IDs ourselves. Logseq
|
|
148
|
+
// auto-assigns them; if the caller smuggled one in, strip it.
|
|
149
|
+
body = body.replace(/^\s*id::\s*[^\n]+\n/gm, '');
|
|
150
|
+
await writeFile(abs, body, 'utf-8');
|
|
151
|
+
}
|
|
152
|
+
async list(prefix) {
|
|
153
|
+
if (typeof prefix !== 'string') {
|
|
154
|
+
throw new Error('storage(logseq): list prefix must be a string');
|
|
155
|
+
}
|
|
156
|
+
if (prefix.length > 0)
|
|
157
|
+
this.resolveSafe(prefix);
|
|
158
|
+
if (!existsSync(this.graph))
|
|
159
|
+
return [];
|
|
160
|
+
const out = [];
|
|
161
|
+
await walkInto(this.graph, this.graph, prefix, out);
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
async delete(path) {
|
|
165
|
+
const abs = this.resolveSafe(path);
|
|
166
|
+
if (!existsSync(abs))
|
|
167
|
+
return;
|
|
168
|
+
await rm(abs, { force: true });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function walkInto(root, dir, prefix, out) {
|
|
172
|
+
let entries;
|
|
173
|
+
try {
|
|
174
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
for (const e of entries) {
|
|
180
|
+
// Skip the graph config directory at the root level
|
|
181
|
+
if (e.name === LOGSEQ_CONFIG_DIR && dir === root)
|
|
182
|
+
continue;
|
|
183
|
+
const full = join(dir, e.name);
|
|
184
|
+
const rel = relative(root, full).split(sep).join('/');
|
|
185
|
+
if (e.isDirectory()) {
|
|
186
|
+
if (prefix === '' || rel.startsWith(prefix) || prefix.startsWith(rel + '/')) {
|
|
187
|
+
await walkInto(root, full, prefix, out);
|
|
188
|
+
}
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (!e.isFile())
|
|
192
|
+
continue;
|
|
193
|
+
if (prefix !== '' && !rel.startsWith(prefix))
|
|
194
|
+
continue;
|
|
195
|
+
let size;
|
|
196
|
+
let modifiedAt;
|
|
197
|
+
try {
|
|
198
|
+
const s = await stat(full);
|
|
199
|
+
size = s.size;
|
|
200
|
+
modifiedAt = s.mtime;
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// ignore
|
|
204
|
+
}
|
|
205
|
+
const entry = { path: rel };
|
|
206
|
+
if (size !== undefined)
|
|
207
|
+
entry.size = size;
|
|
208
|
+
if (modifiedAt !== undefined)
|
|
209
|
+
entry.modifiedAt = modifiedAt;
|
|
210
|
+
out.push(entry);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function expandHome(p) {
|
|
214
|
+
if (p.startsWith('~/')) {
|
|
215
|
+
const { homedir } = require('os');
|
|
216
|
+
return join(homedir(), p.slice(2));
|
|
217
|
+
}
|
|
218
|
+
if (p === '~') {
|
|
219
|
+
const { homedir } = require('os');
|
|
220
|
+
return homedir();
|
|
221
|
+
}
|
|
222
|
+
return p;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* If `content` starts with a `---` YAML block, parse the block and
|
|
226
|
+
* return the parsed fields plus the body without the YAML. Otherwise
|
|
227
|
+
* return null.
|
|
228
|
+
*
|
|
229
|
+
* Minimal parser — handles `key: value` and `key: [a, b, c]` lines.
|
|
230
|
+
* Anything more complex falls back to dropping the value as a string.
|
|
231
|
+
*/
|
|
232
|
+
function extractYamlFrontmatter(content) {
|
|
233
|
+
const match = /^---\s*\r?\n([\s\S]*?)\r?\n---\s*\r?\n?([\s\S]*)$/.exec(content);
|
|
234
|
+
if (!match)
|
|
235
|
+
return null;
|
|
236
|
+
const [, yamlBody, body] = match;
|
|
237
|
+
const fields = {};
|
|
238
|
+
for (const line of yamlBody.split(/\r?\n/)) {
|
|
239
|
+
if (line.trim().length === 0 || line.trim().startsWith('#'))
|
|
240
|
+
continue;
|
|
241
|
+
const m = /^([A-Za-z_][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
|
|
242
|
+
if (!m)
|
|
243
|
+
continue;
|
|
244
|
+
const [, k, rawV] = m;
|
|
245
|
+
fields[k] = parseYamlScalar(rawV.trim());
|
|
246
|
+
}
|
|
247
|
+
return { fields, body };
|
|
248
|
+
}
|
|
249
|
+
function parseYamlScalar(raw) {
|
|
250
|
+
if (raw === '')
|
|
251
|
+
return '';
|
|
252
|
+
if (raw === 'true')
|
|
253
|
+
return true;
|
|
254
|
+
if (raw === 'false')
|
|
255
|
+
return false;
|
|
256
|
+
if (raw === 'null' || raw === '~')
|
|
257
|
+
return null;
|
|
258
|
+
if (/^-?\d+$/.test(raw))
|
|
259
|
+
return Number(raw);
|
|
260
|
+
if (/^-?\d+\.\d+$/.test(raw))
|
|
261
|
+
return Number(raw);
|
|
262
|
+
// Quoted string
|
|
263
|
+
if ((raw.startsWith('"') && raw.endsWith('"')) || (raw.startsWith("'") && raw.endsWith("'"))) {
|
|
264
|
+
try {
|
|
265
|
+
return JSON.parse(raw.replace(/^'/, '"').replace(/'$/, '"'));
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return raw.slice(1, -1);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Inline array
|
|
272
|
+
if (raw.startsWith('[') && raw.endsWith(']')) {
|
|
273
|
+
return raw
|
|
274
|
+
.slice(1, -1)
|
|
275
|
+
.split(',')
|
|
276
|
+
.map((s) => s.trim())
|
|
277
|
+
.filter((s) => s.length > 0)
|
|
278
|
+
.map(parseYamlScalar);
|
|
279
|
+
}
|
|
280
|
+
return raw;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Render a flat object as Logseq page-level properties:
|
|
284
|
+
*
|
|
285
|
+
* tags:: ai, note
|
|
286
|
+
* created:: 2026-04-28
|
|
287
|
+
*
|
|
288
|
+
* <body follows here>
|
|
289
|
+
*/
|
|
290
|
+
function renderPageProperties(fields) {
|
|
291
|
+
const lines = [];
|
|
292
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
293
|
+
lines.push(`${k}:: ${formatLogseqValue(v)}`);
|
|
294
|
+
}
|
|
295
|
+
lines.push('');
|
|
296
|
+
return lines.join('\n');
|
|
297
|
+
}
|
|
298
|
+
function formatLogseqValue(v) {
|
|
299
|
+
if (v === null || v === undefined)
|
|
300
|
+
return '';
|
|
301
|
+
if (typeof v === 'boolean' || typeof v === 'number')
|
|
302
|
+
return String(v);
|
|
303
|
+
if (Array.isArray(v))
|
|
304
|
+
return v.map((x) => formatLogseqValue(x)).join(', ');
|
|
305
|
+
if (typeof v === 'string')
|
|
306
|
+
return v;
|
|
307
|
+
return JSON.stringify(v);
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=logseq.js.map
|