@hasna/knowledge 0.2.25 → 0.2.27

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.
@@ -159,6 +159,30 @@ s3://<knowledge-bucket>/<org>/<project>/knowledge/
159
159
  wiki/
160
160
  ```
161
161
 
162
+ Hasna XYZ production uses the canonical open-source knowledge bucket and app
163
+ path-compatible prefix:
164
+
165
+ ```text
166
+ s3://hasna-xyz-opensource-knowledge-prod/.hasna/apps/knowledge/
167
+ ```
168
+
169
+ The app config can be materialized with:
170
+
171
+ ```bash
172
+ open-knowledge setup --mode hosted --canonical-hasna-xyz --scope project --json
173
+ ```
174
+
175
+ The canonical metadata-only secret paths are:
176
+
177
+ ```text
178
+ hasna/xyz/opensource/knowledge/prod/env
179
+ hasna/xyz/opensource/knowledge/prod/aws
180
+ hasna/xyz/opensource/knowledge/prod/s3
181
+ ```
182
+
183
+ `hasna/xyz/opensource/knowledge/prod/rds` is reserved for a future hosted
184
+ runtime database if the wrapper provisions one.
185
+
162
186
  Raw files still route through `open-files`. Knowledge S3 storage is for derived
163
187
  artifacts such as wiki pages, index shards, schema versions, logs, exports, and
164
188
  run outputs.
@@ -86,6 +86,14 @@ The OSS package may know a storage contract, bucket name, prefix, region, and
86
86
  profile. It must not contain tenant secret values, connector credentials, RDS
87
87
  passwords, hosted KMS key material, or privileged AWS role assumptions.
88
88
 
89
+ For Hasna XYZ production, the OSS contract names
90
+ `hasna-xyz-opensource-knowledge-prod` and prefix
91
+ `.hasna/apps/knowledge/`, plus metadata-only secret paths under
92
+ `hasna/xyz/opensource/knowledge/prod/{env,aws,s3}`. Hosted code is responsible
93
+ for resolving those secrets, assuming AWS roles, enforcing tenant prefixes, and
94
+ provisioning `hasna/xyz/opensource/knowledge/prod/rds` only if a hosted runtime
95
+ database is introduced.
96
+
89
97
  Generated artifacts are safe to sync remotely only when they remain derived:
90
98
  wiki pages, index shards, schema files, logs, exports, run payloads, embeddings,
91
99
  and citation metadata. Raw source bytes stay in `open-files`.
@@ -0,0 +1,127 @@
1
+ # Canonical Secrets Bootstrap Evidence: 2026-06-08
2
+
3
+ Scope: canonical Hasna XYZ app secret paths in account `hasna-xyz-infra`
4
+ (`789877399345`), region `us-east-1`, mirrored into the local `spark02`
5
+ `secrets` vault.
6
+
7
+ This note records names and migration intent only. It intentionally does not
8
+ include secret payloads, passwords, API keys, connection strings, or `.env`
9
+ contents.
10
+
11
+ ## Ownership Rule
12
+
13
+ App-owned runtime/config secrets use:
14
+
15
+ ```txt
16
+ hasna/xyz/{app_type}/{app}/prod/{component}
17
+ ```
18
+
19
+ Shared infra/admin pointers use an infra-owned path:
20
+
21
+ ```txt
22
+ hasna/xyz/infra/{resource_group}/prod/{component}/{role}
23
+ ```
24
+
25
+ Legacy master credentials copied for migration are suffixed with
26
+ `legacy-master`. They are deprecation inputs for migration jobs, not the clean
27
+ runtime secret names new app code should read.
28
+
29
+ ## Open Files
30
+
31
+ Created or verified in AWS Secrets Manager and the local vault:
32
+
33
+ ```txt
34
+ hasna/xyz/opensource/files/prod/env
35
+ hasna/xyz/opensource/files/prod/aws
36
+ hasna/xyz/opensource/files/prod/s3
37
+ hasna/xyz/opensource/files/prod/rds
38
+ ```
39
+
40
+ Meaning:
41
+
42
+ - `env`: app environment configuration.
43
+ - `aws`: AWS account/profile/region and related app config metadata.
44
+ - `s3`: canonical app storage bucket metadata for
45
+ `hasna-xyz-opensource-files-prod`.
46
+ - `rds`: app runtime database connection fields for database `files` and role
47
+ `files_app`.
48
+
49
+ The `aws` and `s3` entries are metadata-only. They do not contain access keys or
50
+ tokens.
51
+
52
+ ## Open Knowledge
53
+
54
+ Created or verified in AWS Secrets Manager and the local vault:
55
+
56
+ ```txt
57
+ hasna/xyz/opensource/knowledge/prod/env
58
+ hasna/xyz/opensource/knowledge/prod/aws
59
+ hasna/xyz/opensource/knowledge/prod/s3
60
+ ```
61
+
62
+ Meaning:
63
+
64
+ - `env`: open-knowledge production app config metadata.
65
+ - `aws`: AWS account/profile/region and related app config metadata.
66
+ - `s3`: canonical app storage bucket metadata for
67
+ `hasna-xyz-opensource-knowledge-prod`.
68
+
69
+ No `hasna/xyz/opensource/knowledge/prod/rds` secret was created in this pass.
70
+ The OSS package is local-first and currently uses SQLite for local knowledge
71
+ state. If a hosted wrapper later provisions an app database, the app-owned
72
+ runtime secret should use:
73
+
74
+ ```txt
75
+ hasna/xyz/opensource/knowledge/prod/rds
76
+ ```
77
+
78
+ ## Legacy Secret Mapping
79
+
80
+ Mapped legacy AWS Secrets Manager names to canonical ownership paths:
81
+
82
+ | Legacy name | Canonical path | Use |
83
+ | --- | --- | --- |
84
+ | `prod/microservice/rds/master` | `hasna/xyz/opensource/microservices/prod/rds/legacy-master` | Migration-only legacy master alias. |
85
+ | `prod/connect/rds/master` | `hasna/xyz/opensource/connectors/prod/rds/legacy-master` | Migration-only legacy master alias. |
86
+ | `internalapps/prod/rds/master` | `hasna/xyz/infra/apps/prod/postgres/legacy-internalapps-master` | Migration-only legacy shared/admin alias. |
87
+ | `internalapps/prod/iapp-news/env` | `hasna/xyz/internalapp/news/prod/env` | Canonical app env path for internalapp `news`. |
88
+
89
+ The three RDS aliases preserve old master credential payloads under explicit
90
+ legacy names so migration jobs can read them without relying on noncanonical
91
+ paths. They should not be used as the final runtime credentials for new app
92
+ code. Clean app runtime database credentials should be provisioned under
93
+ app-owned paths such as:
94
+
95
+ ```txt
96
+ hasna/xyz/opensource/files/prod/rds
97
+ hasna/xyz/internalapp/news/prod/rds
98
+ ```
99
+
100
+ The shared canonical Postgres admin pointer remains:
101
+
102
+ ```txt
103
+ hasna/xyz/infra/apps/prod/postgres/master
104
+ ```
105
+
106
+ ## Verification
107
+
108
+ AWS Secrets Manager name-only verification returned these canonical entries:
109
+
110
+ ```txt
111
+ hasna/xyz/infra/apps/prod/postgres/legacy-internalapps-master
112
+ hasna/xyz/infra/apps/prod/postgres/master
113
+ hasna/xyz/internalapp/news/prod/env
114
+ hasna/xyz/opensource/connectors/prod/rds/legacy-master
115
+ hasna/xyz/opensource/files/prod/aws
116
+ hasna/xyz/opensource/files/prod/env
117
+ hasna/xyz/opensource/files/prod/rds
118
+ hasna/xyz/opensource/files/prod/s3
119
+ hasna/xyz/opensource/knowledge/prod/aws
120
+ hasna/xyz/opensource/knowledge/prod/env
121
+ hasna/xyz/opensource/knowledge/prod/s3
122
+ hasna/xyz/opensource/microservices/prod/rds/legacy-master
123
+ ```
124
+
125
+ Local `secrets list` verification returned the same names with redacted values.
126
+
127
+ No secret values were printed during creation or verification.
@@ -0,0 +1,176 @@
1
+ # Company Wiki Workflow
2
+
3
+ This workflow shows how to use `open-knowledge` as an AI-native company wiki
4
+ layer over source files owned by `open-files`.
5
+
6
+ ## 1. Initialize The Project Workspace
7
+
8
+ ```bash
9
+ open-knowledge paths --scope project --json
10
+ open-knowledge db init --scope project --json
11
+ open-knowledge wiki init --scope project --json
12
+ ```
13
+
14
+ Project state is created under:
15
+
16
+ ```text
17
+ .hasna/apps/knowledge/
18
+ config.json
19
+ knowledge.db
20
+ artifacts/
21
+ indexes/
22
+ logs/
23
+ runs/
24
+ schemas/
25
+ wiki/
26
+ ```
27
+
28
+ ## 2. Import Source Metadata From Open-Files
29
+
30
+ Use an `open-files` manifest with source refs, revisions, permissions, hashes,
31
+ and extracted text:
32
+
33
+ ```json
34
+ {
35
+ "source_ref": "open-files://file/file_handbook/revision/rev_20260608",
36
+ "file_id": "file_handbook",
37
+ "path": "Handbook/Policy.md",
38
+ "name": "Policy.md",
39
+ "mime": "text/markdown",
40
+ "hash": "sha256:...",
41
+ "status": "active",
42
+ "permissions": {
43
+ "mode": "read_only",
44
+ "allowed_purposes": ["knowledge_answer", "knowledge_index"]
45
+ },
46
+ "extracted_text": "Policy text..."
47
+ }
48
+ ```
49
+
50
+ Then ingest it:
51
+
52
+ ```bash
53
+ open-knowledge ingest manifest ./open-files-manifest.jsonl --scope project --json
54
+ ```
55
+
56
+ The knowledge app stores source refs, revisions, redacted chunks, offsets, and
57
+ citations. It does not store raw source bytes or connector credentials.
58
+
59
+ ## 3. Search And Build Context
60
+
61
+ Run local keyword/catalog search first:
62
+
63
+ ```bash
64
+ open-knowledge search "expense policy" --scope project --json
65
+ ```
66
+
67
+ Add deterministic local semantic indexing for a smoke test:
68
+
69
+ ```bash
70
+ open-knowledge embeddings index --scope project --fake --dimensions 8 --json
71
+ open-knowledge search "expense policy" --scope project --semantic --fake --dimensions 8 --json
72
+ ```
73
+
74
+ Ask for an agent-ready context pack:
75
+
76
+ ```bash
77
+ open-knowledge search "expense policy" --scope project --context --json
78
+ ```
79
+
80
+ ## 4. Answer With Citations
81
+
82
+ Create a local citation draft:
83
+
84
+ ```bash
85
+ knowledge "How do we approve expenses?" --scope project --json
86
+ ```
87
+
88
+ Use provider generation explicitly:
89
+
90
+ ```bash
91
+ OPENAI_API_KEY=... \
92
+ knowledge "How do we approve expenses?" \
93
+ --scope project \
94
+ --generate \
95
+ --model openai:gpt-5-mini \
96
+ --json
97
+ ```
98
+
99
+ Every prompt run records `runs`, `run_events`, provider/model metadata, usage,
100
+ citations, and proposed wiki updates.
101
+
102
+ ## 5. Compile Durable Wiki Pages
103
+
104
+ Compile a cited page from indexed chunks:
105
+
106
+ ```bash
107
+ open-knowledge wiki compile "expense policy" \
108
+ --title "Expense Policy" \
109
+ --scope project \
110
+ --json
111
+ ```
112
+
113
+ File an approved answer note:
114
+
115
+ ```bash
116
+ open-knowledge wiki file-answer "How do we approve expenses?" \
117
+ --content "Use manager approval and cite the policy source." \
118
+ --approve-write \
119
+ --scope project \
120
+ --json
121
+ ```
122
+
123
+ Lint the generated wiki:
124
+
125
+ ```bash
126
+ open-knowledge wiki lint --scope project --json
127
+ ```
128
+
129
+ Generated pages are written through the artifact store and cataloged in
130
+ `knowledge.db`; index rows and logs are sharded rather than stored in one large
131
+ Markdown file.
132
+
133
+ ## 6. Keep Sources Fresh
134
+
135
+ Consume open-files outbox events after source changes:
136
+
137
+ ```bash
138
+ open-knowledge reindex outbox ./open-files-outbox.jsonl --scope project --json
139
+ open-knowledge reindex enqueue --scope project --json
140
+ open-knowledge reindex embeddings --scope project --fake --dimensions 8 --json
141
+ ```
142
+
143
+ This invalidates stale source chunks and refreshes embeddings without losing the
144
+ source refs and citation provenance.
145
+
146
+ ## 7. Expose The Wiki To Agents Through MCP
147
+
148
+ Run MCP over stdio:
149
+
150
+ ```bash
151
+ open-knowledge-mcp
152
+ ```
153
+
154
+ Or run local Streamable HTTP:
155
+
156
+ ```bash
157
+ open-knowledge-mcp --http --port 8819
158
+ ```
159
+
160
+ Agents should prefer stable tools such as `knowledge_search`, `knowledge_ask`,
161
+ `knowledge_build`, `knowledge_get`, `knowledge_lint`, and
162
+ `knowledge_run_status`. They can inspect project resources such as
163
+ `knowledge://project/wiki/pages`, `knowledge://project/runs`, and
164
+ `knowledge://project/open-files`.
165
+
166
+ ## 8. Optional Hosted And S3 Mode
167
+
168
+ Hosted mode is only a remote client boundary:
169
+
170
+ ```bash
171
+ open-knowledge setup --mode hosted --api-url https://knowledge.hasna.xyz --scope project --json
172
+ open-knowledge remote contracts --scope project --json
173
+ ```
174
+
175
+ Generated artifacts may use S3 when configured, but raw source files still stay
176
+ in `open-files`.
@@ -0,0 +1,151 @@
1
+ # JSON To SQLite Migration
2
+
3
+ `open-knowledge` began as a simple JSON note store. Current project mode uses a
4
+ Hasna app workspace and a versioned SQLite catalog:
5
+
6
+ ```text
7
+ .hasna/apps/knowledge/
8
+ db.json
9
+ knowledge.db
10
+ artifacts/
11
+ indexes/
12
+ logs/
13
+ runs/
14
+ schemas/
15
+ wiki/
16
+ ```
17
+
18
+ The JSON store remains available for compatibility with note commands such as
19
+ `add`, `list`, `get`, `update`, `delete`, and `export`. The SQLite catalog is
20
+ used for source refs, source revisions, chunks, citations, embeddings, wiki
21
+ pages, generated artifacts, runs, audit events, and reindex jobs.
22
+
23
+ ## What Migrates Automatically
24
+
25
+ Global legacy notes are migrated on first use:
26
+
27
+ ```text
28
+ ~/.open-knowledge/db.json
29
+ ```
30
+
31
+ to:
32
+
33
+ ```text
34
+ ~/.hasna/apps/knowledge/db.json
35
+ ```
36
+
37
+ This happens only when the new Hasna JSON store does not already exist. The
38
+ legacy file is not deleted.
39
+
40
+ Project mode writes directly to:
41
+
42
+ ```text
43
+ <project>/.hasna/apps/knowledge/db.json
44
+ ```
45
+
46
+ when compatibility note commands are used with `--scope project`.
47
+
48
+ ## What Requires Explicit Ingestion
49
+
50
+ SQLite knowledge records are not inferred from old JSON notes automatically.
51
+ Use explicit commands so provenance, permissions, citations, and redaction are
52
+ recorded correctly.
53
+
54
+ Initialize the project catalog:
55
+
56
+ ```bash
57
+ open-knowledge db init --scope project --json
58
+ open-knowledge wiki init --scope project --json
59
+ ```
60
+
61
+ Import open-files manifests:
62
+
63
+ ```bash
64
+ open-knowledge ingest manifest ./open-files-manifest.jsonl --scope project --json
65
+ ```
66
+
67
+ Import one allowed source ref:
68
+
69
+ ```bash
70
+ open-knowledge ingest source file:///absolute/path/to/handbook.md \
71
+ --purpose knowledge_index \
72
+ --scope project \
73
+ --json
74
+ ```
75
+
76
+ Resolve indexed source evidence:
77
+
78
+ ```bash
79
+ open-knowledge source resolve open-files://file/file_123/revision/rev_456 \
80
+ --purpose knowledge_answer \
81
+ --scope project \
82
+ --json
83
+ ```
84
+
85
+ ## Recommended Migration Path
86
+
87
+ 1. Keep the legacy JSON note store as an exportable compatibility layer.
88
+ 2. Run `open-knowledge paths --scope project --json` and confirm the project
89
+ workspace is `.hasna/apps/knowledge`.
90
+ 3. Initialize `knowledge.db` with `open-knowledge db init --scope project`.
91
+ 4. Ingest source manifests from `open-files` rather than copying raw files into
92
+ `open-knowledge`.
93
+ 5. Run `open-knowledge search --scope project --json` to verify source chunks.
94
+ 6. Run `open-knowledge wiki compile` for durable cited pages.
95
+ 7. Run `open-knowledge wiki lint --scope project --json` before treating pages
96
+ as company knowledge.
97
+ 8. Use `open-knowledge export --format jsonl` if legacy notes need to be
98
+ archived or transformed outside the app.
99
+
100
+ ## JSON Output Contracts
101
+
102
+ Use `--json` during migration. Commands return stable objects with `ok: true`
103
+ when successful and command-specific fields such as:
104
+
105
+ - `paths`: workspace paths and config.
106
+ - `db stats`: schema version and table counts.
107
+ - `ingest manifest`: sources, revisions, chunks, redactions, and skipped rows.
108
+ - `source resolve`: read-only source metadata, chunks, citations, and evidence.
109
+ - `search --context`: excerpts, citations, graph evidence, and warnings.
110
+ - `ask|build`: run id, answer, context, citations, proposed wiki updates, write
111
+ policy, usage, and warnings.
112
+ - `wiki compile`: page id, artifact URI, citations written, index updates, and
113
+ log shard key.
114
+
115
+ ## Safety Rules During Migration
116
+
117
+ - Prefer `open-files://` refs for durable company sources.
118
+ - Keep raw source bytes in `open-files`; do not import them as generated wiki
119
+ artifacts.
120
+ - Enable S3 reads only for allowed buckets:
121
+
122
+ ```bash
123
+ HASNA_KNOWLEDGE_ALLOW_S3_READS=1 \
124
+ HASNA_KNOWLEDGE_ALLOWED_S3_BUCKETS=my-bucket \
125
+ open-knowledge ingest manifest s3://my-bucket/path/manifest.jsonl \
126
+ --scope project \
127
+ --json
128
+ ```
129
+
130
+ - Enable web search only when current external context is required:
131
+
132
+ ```bash
133
+ HASNA_KNOWLEDGE_WEB_SEARCH=1 \
134
+ open-knowledge web search "current policy source" --provider openai --json
135
+ ```
136
+
137
+ - Use `--approve-write` only when a generated wiki artifact should be durable.
138
+
139
+ ## Hosted Migration
140
+
141
+ Hosted mode should not change local migration semantics. It only records a
142
+ remote API boundary:
143
+
144
+ ```bash
145
+ open-knowledge setup --mode hosted --api-url https://knowledge.hasna.xyz --scope project --json
146
+ open-knowledge remote contracts --scope project --json
147
+ ```
148
+
149
+ A SaaS wrapper can later sync generated artifacts, run jobs, enforce tenant ACLs,
150
+ and store artifacts in S3, but the local package remains usable without a hosted
151
+ account.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/knowledge",
3
- "version": "0.2.25",
3
+ "version": "0.2.27",
4
4
  "description": "Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -57,6 +57,7 @@ interface Flags {
57
57
  provider?: string;
58
58
  mode?: string;
59
59
  apiUrl?: string;
60
+ canonicalHasnaXyz?: boolean;
60
61
  apiKey?: string;
61
62
  email?: string;
62
63
  org?: string;
@@ -125,6 +126,7 @@ function parseArgs(argv: string[]): ParseResult {
125
126
  case '--provider': flags.provider = argv[i + 1]; i += 1; break;
126
127
  case '--mode': flags.mode = argv[i + 1]; i += 1; break;
127
128
  case '--api-url': flags.apiUrl = argv[i + 1]; i += 1; break;
129
+ case '--canonical-hasna-xyz': flags.canonicalHasnaXyz = true; break;
128
130
  case '--api-key': flags.apiKey = argv[i + 1]; i += 1; break;
129
131
  case '--email': flags.email = argv[i + 1]; i += 1; break;
130
132
  case '--org': flags.org = argv[i + 1]; i += 1; break;
@@ -204,7 +206,7 @@ Commands:
204
206
  dedupe Remove duplicate items by title+content (requires --yes)
205
207
  stats Show knowledge base statistics
206
208
  paths Show resolved workspace/store paths
207
- setup Configure local or hosted mode
209
+ setup Configure local, hosted, or canonical Hasna XYZ S3 mode
208
210
  auth login|whoami|logout Manage hosted API credentials
209
211
  remote contracts|status Inspect hosted client contracts/readiness
210
212
  storage status|validate Inspect local/S3 artifact storage contract
@@ -299,7 +301,7 @@ function printCommandHelp(command: string): void {
299
301
  if (command === 'dedupe') { console.log('Usage: open-knowledge dedupe --yes [--json]'); return; }
300
302
  if (command === 'stats') { console.log('Usage: open-knowledge stats [--json]'); return; }
301
303
  if (command === 'paths') { console.log('Usage: open-knowledge paths [--scope local|global|project] [--json]'); return; }
302
- if (command === 'setup') { console.log('Usage: open-knowledge setup --mode local|hosted [--api-url https://...] [--scope local|global|project] [--json]'); return; }
304
+ if (command === 'setup') { console.log('Usage: open-knowledge setup --mode local|hosted [--api-url https://...] [--canonical-hasna-xyz] [--scope local|global|project] [--json]'); return; }
303
305
  if (command === 'auth') { console.log('Usage: open-knowledge auth login|whoami|logout [--api-key <key>] [--email <email>] [--org <slug>] [--api-url https://...] [--scope local|global|project] [--json]'); return; }
304
306
  if (command === 'remote') { console.log('Usage: open-knowledge remote contracts|status [--scope local|global|project] [--json]'); return; }
305
307
  if (command === 'storage') { console.log('Usage: open-knowledge storage status|validate [--scope local|global|project] [--json]'); return; }
@@ -358,11 +360,11 @@ async function run(argv: string[]): Promise<void> {
358
360
  if (flags.completions) {
359
361
  const shell = flags.completions;
360
362
  if (shell === 'bash') {
361
- console.log(`_open_knowledge() { local cur; cur="${"$"}{COMP_WORDS[COMP_CWORD]}"; COMPREPLY=($(compgen -W "add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge --json --yes --help --version --desc --page --limit --search --sort --id --store --title --content --url --tag --format --completions --purpose --model --dimensions --semantic --context --generate --approve-write --provider --mode --api-url --api-key --email --org --org-id --user-id --domain --file-results --full --fake --no-color --scope --archived --include-archived" -- "$cur")); }; complete -F _open_knowledge open-knowledge`);
363
+ console.log(`_open_knowledge() { local cur; cur="${"$"}{COMP_WORDS[COMP_CWORD]}"; COMPREPLY=($(compgen -W "add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge --json --yes --help --version --desc --page --limit --search --sort --id --store --title --content --url --tag --format --completions --purpose --model --dimensions --semantic --context --generate --approve-write --provider --mode --api-url --canonical-hasna-xyz --api-key --email --org --org-id --user-id --domain --file-results --full --fake --no-color --scope --archived --include-archived" -- "$cur")); }; complete -F _open_knowledge open-knowledge`);
362
364
  } else if (shell === 'zsh') {
363
- console.log(`#compdef open-knowledge\n_open_knowledge() { _arguments -C "1: :(add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge)" "(--json)--json" "(--yes)-y" "(--help)--help" "(--version)--version" "(--desc)--desc" "(--archived)--archived" "(--include-archived)--include-archived" "(--semantic)--semantic" "(--context)--context" "(--generate)--generate" "(--approve-write)--approve-write" "(--file-results)--file-results" "(--full)--full" "(--fake)--fake" "(-p --page)"{-p,--page}"[page number]:number:" "(-l --limit)"{-l,--limit}"[items per page]:number:" "(-s --search)"{-s,--search}"[search text]:text:" "(--sort)--sort"\{created,title\}:" "(--id)--id[item id]:id:" "(--store)--store[store path]:path:" "(--title)--title[new title]:" "(--content)--content[new content]:" "(--url)--url[source url]:" "(-t --tag)"{-t,--tag}"[tag]:tag:" "(--format)--format[json|jsonl]:" "(--completions)--completions[output completions]:shell:(bash zsh fish):" "(--purpose)--purpose[purpose]:" "(--model)--model[model ref]:" "(--dimensions)--dimensions[embedding dimensions]:number:" "(--provider)--provider[provider]:" "(--mode)--mode"\{local,hosted\}:" "(--api-url)--api-url[hosted API URL]:" "(--api-key)--api-key[hosted API key]:" "(--email)--email[email]:" "(--org)--org[org slug]:" "(--org-id)--org-id[org id]:" "(--user-id)--user-id[user id]:" "(--domain)--domain[domain]:" "(--no-color)--no-color[disable color]" "(--scope)--scope"\{local,global,project\}:" }; _open_knowledge`);
365
+ console.log(`#compdef open-knowledge\n_open_knowledge() { _arguments -C "1: :(add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge)" "(--json)--json" "(--yes)-y" "(--help)--help" "(--version)--version" "(--desc)--desc" "(--archived)--archived" "(--include-archived)--include-archived" "(--semantic)--semantic" "(--context)--context" "(--generate)--generate" "(--approve-write)--approve-write" "(--canonical-hasna-xyz)--canonical-hasna-xyz" "(--file-results)--file-results" "(--full)--full" "(--fake)--fake" "(-p --page)"{-p,--page}"[page number]:number:" "(-l --limit)"{-l,--limit}"[items per page]:number:" "(-s --search)"{-s,--search}"[search text]:text:" "(--sort)--sort"\{created,title\}:" "(--id)--id[item id]:id:" "(--store)--store[store path]:path:" "(--title)--title[new title]:" "(--content)--content[new content]:" "(--url)--url[source url]:" "(-t --tag)"{-t,--tag}"[tag]:tag:" "(--format)--format[json|jsonl]:" "(--completions)--completions[output completions]:shell:(bash zsh fish):" "(--purpose)--purpose[purpose]:" "(--model)--model[model ref]:" "(--dimensions)--dimensions[embedding dimensions]:number:" "(--provider)--provider[provider]:" "(--mode)--mode"\{local,hosted\}:" "(--api-url)--api-url[hosted API URL]:" "(--api-key)--api-key[hosted API key]:" "(--email)--email[email]:" "(--org)--org[org slug]:" "(--org-id)--org-id[org id]:" "(--user-id)--user-id[user id]:" "(--domain)--domain[domain]:" "(--no-color)--no-color[disable color]" "(--scope)--scope"\{local,global,project\}:" }; _open_knowledge`);
364
366
  } else if (shell === 'fish') {
365
- console.log(`complete -c open-knowledge -f; complete -c open-knowledge -a "add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge"; complete -c open-knowledge -l json; complete -c open-knowledge -l yes -s y; complete -c open-knowledge -l help -s h; complete -c open-knowledge -l version -s v; complete -c open-knowledge -l desc; complete -c open-knowledge -l archived; complete -c open-knowledge -l include-archived; complete -c open-knowledge -l semantic; complete -c open-knowledge -l context; complete -c open-knowledge -l generate; complete -c open-knowledge -l approve-write; complete -c open-knowledge -l provider; complete -c open-knowledge -l mode; complete -c open-knowledge -l api-url; complete -c open-knowledge -l api-key; complete -c open-knowledge -l email; complete -c open-knowledge -l org; complete -c open-knowledge -l org-id; complete -c open-knowledge -l user-id; complete -c open-knowledge -l domain; complete -c open-knowledge -l file-results; complete -c open-knowledge -l full; complete -c open-knowledge -l fake; complete -c open-knowledge -s p -l page; complete -c open-knowledge -s l -l limit; complete -c open-knowledge -s s -l search; complete -c open-knowledge -l sort; complete -c open-knowledge -l id; complete -c open-knowledge -l store; complete -c open-knowledge -l title; complete -c open-knowledge -l content; complete -c open-knowledge -l url; complete -c open-knowledge -s t -l tag; complete -c open-knowledge -l format; complete -c open-knowledge -l completions; complete -c open-knowledge -l purpose; complete -c open-knowledge -l model; complete -c open-knowledge -l dimensions; complete -c open-knowledge -l no-color; complete -c open-knowledge -l scope -a "local global project"`);
367
+ console.log(`complete -c open-knowledge -f; complete -c open-knowledge -a "add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge"; complete -c open-knowledge -l json; complete -c open-knowledge -l yes -s y; complete -c open-knowledge -l help -s h; complete -c open-knowledge -l version -s v; complete -c open-knowledge -l desc; complete -c open-knowledge -l archived; complete -c open-knowledge -l include-archived; complete -c open-knowledge -l semantic; complete -c open-knowledge -l context; complete -c open-knowledge -l generate; complete -c open-knowledge -l approve-write; complete -c open-knowledge -l canonical-hasna-xyz; complete -c open-knowledge -l provider; complete -c open-knowledge -l mode; complete -c open-knowledge -l api-url; complete -c open-knowledge -l api-key; complete -c open-knowledge -l email; complete -c open-knowledge -l org; complete -c open-knowledge -l org-id; complete -c open-knowledge -l user-id; complete -c open-knowledge -l domain; complete -c open-knowledge -l file-results; complete -c open-knowledge -l full; complete -c open-knowledge -l fake; complete -c open-knowledge -s p -l page; complete -c open-knowledge -s l -l limit; complete -c open-knowledge -s s -l search; complete -c open-knowledge -l sort; complete -c open-knowledge -l id; complete -c open-knowledge -l store; complete -c open-knowledge -l title; complete -c open-knowledge -l content; complete -c open-knowledge -l url; complete -c open-knowledge -s t -l tag; complete -c open-knowledge -l format; complete -c open-knowledge -l completions; complete -c open-knowledge -l purpose; complete -c open-knowledge -l model; complete -c open-knowledge -l dimensions; complete -c open-knowledge -l no-color; complete -c open-knowledge -l scope -a "local global project"`);
366
368
  } else {
367
369
  throw new Error("Invalid --completions value. Use 'bash', 'zsh', or 'fish'.");
368
370
  }
@@ -397,6 +399,7 @@ async function run(argv: string[]): Promise<void> {
397
399
  const result = service.setup({
398
400
  mode: flags.mode,
399
401
  apiUrl: flags.apiUrl,
402
+ canonicalHasnaXyz: flags.canonicalHasnaXyz,
400
403
  });
401
404
  output(result, flags.json);
402
405
  return;
package/src/service.ts CHANGED
@@ -36,6 +36,7 @@ import {
36
36
  } from './storage-contract';
37
37
  import { initializeWikiLayout, recordWikiLayoutCatalog } from './wiki-layout';
38
38
  import {
39
+ canonicalHasnaXyzKnowledgeStorage,
39
40
  ensureKnowledgeWorkspace,
40
41
  readKnowledgeConfig,
41
42
  resolveScopedWorkspace,
@@ -70,6 +71,9 @@ export interface KnowledgeSetupResult {
70
71
  ok: true;
71
72
  mode: KnowledgeConfig['mode'];
72
73
  api_url: string | null;
74
+ storage_type: KnowledgeConfig['storage']['type'];
75
+ artifact_uri_prefix: string;
76
+ canonical_hasna_xyz: StorageContract['canonical_hasna_xyz'];
73
77
  config_path: string;
74
78
  next: string[];
75
79
  message: string;
@@ -130,7 +134,7 @@ export class KnowledgeService {
130
134
  return validateStorageConfig(this.config(), this.ensureWorkspace());
131
135
  }
132
136
 
133
- setup(options: { mode?: string; apiUrl?: string } = {}): KnowledgeSetupResult {
137
+ setup(options: { mode?: string; apiUrl?: string; canonicalHasnaXyz?: boolean } = {}): KnowledgeSetupResult {
134
138
  const workspace = this.ensureWorkspace();
135
139
  const current = this.config();
136
140
  const mode = normalizeMode(options.mode) ?? current.mode;
@@ -146,16 +150,23 @@ export class KnowledgeService {
146
150
  ...(current.hosted ?? {}),
147
151
  ...(apiUrl ? { api_url: apiUrl } : {}),
148
152
  },
153
+ storage: options.canonicalHasnaXyz
154
+ ? canonicalHasnaXyzKnowledgeStorage()
155
+ : current.storage,
149
156
  };
150
157
  writeKnowledgeConfig(workspace.configPath, nextConfig);
151
158
  this.cachedConfig = nextConfig;
159
+ const storage = resolveStorageContract(nextConfig, workspace, this.scope);
152
160
  return {
153
161
  ok: true,
154
162
  mode,
155
163
  api_url: nextConfig.hosted?.api_url ?? null,
164
+ storage_type: nextConfig.storage.type,
165
+ artifact_uri_prefix: storage.artifact_store.uri_prefix,
166
+ canonical_hasna_xyz: storage.canonical_hasna_xyz,
156
167
  config_path: workspace.configPath,
157
168
  next: mode === 'hosted'
158
- ? ['open-knowledge auth login --api-key <key>', 'open-knowledge remote contracts --json']
169
+ ? ['open-knowledge auth login --api-key <key>', 'open-knowledge storage status --json', 'open-knowledge remote contracts --json']
159
170
  : ['open-knowledge search <query>', 'knowledge <prompt>'],
160
171
  message: `Set knowledge mode to ${mode}`,
161
172
  };