@jagilber-org/index-server 1.28.10 → 1.28.19

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 (73) hide show
  1. package/CHANGELOG.md +109 -1
  2. package/CONTRIBUTING.md +13 -0
  3. package/README.md +10 -14
  4. package/dist/config/featureConfig.js +4 -1
  5. package/dist/dashboard/client/admin.html +69 -29
  6. package/dist/dashboard/client/js/admin.embeddings.js +97 -5
  7. package/dist/dashboard/client/js/admin.instructions.js +1 -1
  8. package/dist/dashboard/server/AdminPanel.js +38 -0
  9. package/dist/dashboard/server/ApiRoutes.js +14 -1
  10. package/dist/dashboard/server/routes/embeddings.routes.js +76 -1
  11. package/dist/dashboard/server/routes/instructions.routes.js +4 -11
  12. package/dist/dashboard/server/routes/scripts.routes.js +35 -10
  13. package/dist/dashboard/server/routes/status.routes.js +77 -0
  14. package/dist/models/instruction.d.ts +2 -1
  15. package/dist/models/instruction.js +2 -0
  16. package/dist/schemas/index-server.code-schema.json +52478 -0
  17. package/dist/schemas/index.d.ts +7 -164
  18. package/dist/schemas/index.js +45 -63
  19. package/dist/schemas/instructionSchema.d.ts +46 -0
  20. package/dist/schemas/instructionSchema.js +159 -0
  21. package/{schemas → dist/schemas}/json-schema/instruction-content-type.schema.json +6 -4
  22. package/{schemas → dist/schemas}/json-schema/instruction-instruction-entry.schema.json +6 -4
  23. package/dist/schemas/manifest.json +78 -0
  24. package/dist/server/index-server.js +7 -1
  25. package/dist/services/bootstrapGating.js +2 -2
  26. package/dist/services/handlers/instructions.add.js +18 -0
  27. package/dist/services/handlers/instructions.groom.js +6 -1
  28. package/dist/services/handlers/instructions.import.js +42 -7
  29. package/dist/services/handlers.activation.js +3 -1
  30. package/dist/services/handlers.dashboardConfig.js +2 -1
  31. package/dist/services/handlers.feedback.d.ts +4 -4
  32. package/dist/services/handlers.feedback.js +390 -27
  33. package/dist/services/handlers.instructionSchema.js +73 -31
  34. package/dist/services/handlers.search.js +11 -6
  35. package/dist/services/indexLoader.js +7 -0
  36. package/dist/services/instructionRecordValidation.js +32 -84
  37. package/dist/services/mcpConfig/flagCatalog.d.ts +1 -1
  38. package/dist/services/mcpConfig/flagCatalog.js +2 -0
  39. package/dist/services/mcpConfig/formats.js +2 -6
  40. package/dist/services/seedBootstrap.contentModel.d.ts +13 -0
  41. package/dist/services/seedBootstrap.contentModel.js +166 -0
  42. package/dist/services/seedBootstrap.contentTypes.d.ts +5 -0
  43. package/dist/services/seedBootstrap.contentTypes.js +76 -0
  44. package/dist/services/seedBootstrap.d.ts +1 -0
  45. package/dist/services/seedBootstrap.js +87 -10
  46. package/dist/services/toolRegistry.js +52 -24
  47. package/dist/services/toolRegistry.zod.js +84 -37
  48. package/dist/versioning/schemaVersion.d.ts +1 -1
  49. package/dist/versioning/schemaVersion.js +1 -13
  50. package/package.json +17 -3
  51. package/schemas/index-server.code-schema.json +31019 -25047
  52. package/schemas/instruction.schema.json +16 -6
  53. package/schemas/manifest.json +3 -3
  54. package/scripts/README.md +20 -0
  55. package/scripts/build/README.md +41 -0
  56. package/scripts/build/setup-wizard-paths.mjs +27 -0
  57. package/scripts/build/setup-wizard.mjs +7 -21
  58. package/scripts/client/README.md +26 -0
  59. package/scripts/client/index-server-client.ps1 +203 -0
  60. package/scripts/client/index-server-client.sh +149 -0
  61. package/scripts/client/powershell-mcp-server.ps1 +83 -0
  62. package/scripts/client/powershell-mcp-template.ps1 +85 -0
  63. package/scripts/hooks/README.md +40 -0
  64. package/server.json +2 -2
  65. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-persisted-admin-session.schema.json +0 -0
  66. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-persisted-session-history-entry.schema.json +0 -0
  67. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-persisted-web-socket-connection.schema.json +0 -0
  68. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-session-persistence-config.schema.json +0 -0
  69. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-session-persistence-data.schema.json +0 -0
  70. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-session-persistence-manifest.schema.json +0 -0
  71. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-session-persistence-metadata.schema.json +0 -0
  72. /package/{schemas → dist/schemas}/json-schema/instruction-audience-scope.schema.json +0 -0
  73. /package/{schemas → dist/schemas}/json-schema/instruction-requirement-level.schema.json +0 -0
@@ -132,26 +132,30 @@
132
132
  },
133
133
  "contentType": {
134
134
  "enum": [
135
+ "agent",
136
+ "skill",
135
137
  "instruction",
136
- "template",
138
+ "prompt",
137
139
  "workflow",
138
- "reference",
139
- "example",
140
- "agent"
140
+ "knowledge",
141
+ "template",
142
+ "integration"
141
143
  ],
142
144
  "default": "instruction",
143
- "description": "Content type classification: instruction (actionable guidance), template (scaffolds/error templates), workflow (multi-step processes or runbooks), reference (docs/guides), example (code samples), agent (AI agent definitions/personas). The legacy value chat-session is accepted only by migration/write compatibility and is normalized to workflow before persistence; new records should use workflow."
145
+ "description": "Content type classification: agent (AI agent definitions and personas), skill (packaged agent capabilities or callable skills), instruction (actionable guidance and operating rules), prompt (prompt templates or prompt engineering assets), workflow (multi-step processes or runbooks), knowledge (reference material, examples, concepts, and documentation), template (reusable scaffolds or structured content templates), integration (external system, MCP, API, or tool integration guidance)."
144
146
  },
145
147
  "schemaVersion": {
146
148
  "type": "string",
147
149
  "enum": [
148
- "5"
150
+ "6"
149
151
  ],
152
+ "x-fieldClass": "server-managed",
150
153
  "description": "Internal schema version for migration"
151
154
  },
152
155
  "sourceHash": {
153
156
  "type": "string",
154
157
  "pattern": "^[a-f0-9]{64}$",
158
+ "x-fieldClass": "server-managed",
155
159
  "description": "SHA256 hash of body for drift detection"
156
160
  },
157
161
  "deprecatedBy": {
@@ -161,26 +165,31 @@
161
165
  "createdAt": {
162
166
  "type": "string",
163
167
  "format": "date-time",
168
+ "x-fieldClass": "server-managed",
164
169
  "description": "Creation timestamp (ISO 8601)"
165
170
  },
166
171
  "updatedAt": {
167
172
  "type": "string",
168
173
  "format": "date-time",
174
+ "x-fieldClass": "server-managed",
169
175
  "description": "Last mutation timestamp (ISO 8601)"
170
176
  },
171
177
  "usageCount": {
172
178
  "type": "integer",
173
179
  "minimum": 0,
180
+ "x-fieldClass": "server-managed",
174
181
  "description": "Number of tracked usage events"
175
182
  },
176
183
  "firstSeenTs": {
177
184
  "type": "string",
178
185
  "format": "date-time",
186
+ "x-fieldClass": "server-managed",
179
187
  "description": "Timestamp when usage was first observed (ISO 8601)"
180
188
  },
181
189
  "lastUsedAt": {
182
190
  "type": "string",
183
191
  "format": "date-time",
192
+ "x-fieldClass": "server-managed",
184
193
  "description": "Last usage timestamp (ISO 8601)"
185
194
  },
186
195
  "riskScore": {
@@ -270,6 +279,7 @@
270
279
  "archivedAt": {
271
280
  "type": "string",
272
281
  "format": "date-time",
282
+ "x-fieldClass": "server-managed",
273
283
  "description": "Timestamp when archived (ISO 8601)"
274
284
  },
275
285
  "semanticSummary": {
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "1.26.11",
3
- "generatedAt": "2026-04-30T21:14:01.841Z",
2
+ "version": "1.28.19",
3
+ "generatedAt": "2026-05-11T01:35:27.991Z",
4
4
  "trackedDir": "schemas",
5
5
  "distDir": "dist/schemas",
6
6
  "jsonSchemas": [
@@ -67,7 +67,7 @@
67
67
  "src",
68
68
  "scripts"
69
69
  ],
70
- "fileCount": 245,
70
+ "fileCount": 266,
71
71
  "features": {
72
72
  "jsdoc": true,
73
73
  "lineNumbers": true,
package/scripts/README.md CHANGED
@@ -10,6 +10,7 @@ Organized by purpose. Each subdirectory has a clear role; naming conventions mak
10
10
  | `ci/` | CI-specific runners and validation | — |
11
11
  | `client/` | Client CLI tools and wizards | — |
12
12
  | `deploy/` | Deployment and production ops | `deploy-*`, `smoke-*`, `prod-*` |
13
+ | `dev/` | Dev-server orchestrator + probes (see below) | — |
13
14
  | `diagnostics/` | Adhoc probes, health checks, inspection | `adhoc-*` for one-off probes |
14
15
  | `dist/` | Distributable scripts (shipped to users) | — |
15
16
  | `governance/` | Guards, validators, lint, coverage | `guard-*`, `check-*`, `validate-*` |
@@ -19,6 +20,25 @@ Organized by purpose. Each subdirectory has a clear role; naming conventions mak
19
20
  | `perf/` | Performance baselines, trends, benchmarks | `perf-*`, `benchmark-*`, `stress-*` |
20
21
  | `testing/` | Test runners, helpers, fixtures | `test-*`, `run-*`, `seed-*` |
21
22
 
23
+ Root-level PowerShell files under `scripts/` are compatibility wrappers for
24
+ template-managed paths. Prefer the categorized subdirectory implementation for
25
+ new logic.
26
+
27
+ ## Dev-server layout
28
+
29
+ `dev/` contains the profile-sandboxed dev orchestrator and its probes, organized
30
+ by role:
31
+
32
+ | Subdirectory | Role | Scripts |
33
+ |---|---|---|
34
+ | `dev/` | Orchestrator | `dev-server.ps1` |
35
+ | `dev/transport/` | Shared harness | `mcp-stdio.mjs` |
36
+ | `dev/diagnostic/` | Read-only probes | `info-probe.mjs`, `shape-probe.mjs` |
37
+ | `dev/integrity/` | Mutation + verify probes | `crud-probe.mjs`, `disk-server-consistency.mjs`, `io-matrix.mjs` |
38
+ | `dev/util/` | CLI utilities | `io-helper.mjs` |
39
+
40
+ > See `scripts/dev/README.md` for the full dev-server runbook.
41
+
22
42
  ## Naming Conventions
23
43
 
24
44
  - **`adhoc-*`** — One-off diagnostic probes. Never referenced by CI or `package.json`. Safe to run manually during debugging; safe to delete when no longer needed.
@@ -0,0 +1,41 @@
1
+ # scripts/build
2
+
3
+ Build, release, schema generation, and publishing tools. Scripts here produce
4
+ artifacts — compiled output, docs, schemas, manifests — and drive the release
5
+ pipeline. Most are wired to `npm run` tasks or called by `Invoke-ReleaseWorkflow.ps1`.
6
+
7
+ ## Scripts
8
+
9
+ | Script | Purpose |
10
+ |--------|---------|
11
+ | `build.ps1` | Full build wrapper: `tsc` + asset copy |
12
+ | `ci-build.mjs` | CI-mode build with strict exit codes |
13
+ | `bump-version.mjs` | Bump `package.json` version; commits and tags |
14
+ | `bump-version.ps1` | PowerShell wrapper around `bump-version.mjs` |
15
+ | `copy-dashboard-assets.mjs` | Copy dashboard HTML/JS/CSS into `dist/` |
16
+ | `generate-certs.mjs` | Generate self-signed TLS certs for local HTTPS |
17
+ | `generate-drift-report.mjs` | Compare current schema vs last snapshot; report drift |
18
+ | `generate-manifest.mjs` | Regenerate `schemas/manifest.json` from source |
19
+ | `generate-schemas.mjs` | Regenerate all JSON schemas from TypeScript types |
20
+ | `generate-tools-doc.mjs` | Regenerate `docs/TOOLS-GENERATED.md` from tool registry |
21
+ | `generate-tools-snapshot.mjs` | Capture current tool-registry snapshot for drift checks |
22
+ | `Invoke-ReleaseWorkflow.ps1` | End-to-end release: bump → build → publish → tag |
23
+ | `publish-direct-to-remote.cjs` | Publish build artifacts directly to the remote mirror |
24
+ | `Publish-ToMirror.ps1` | Copy clean-room build to the production mirror path |
25
+ | `set-registry.mjs` | Switch npm registry for publish (scoped packages) |
26
+ | `setup-wizard.mjs` | Interactive first-run setup wizard |
27
+ | `setup-wizard-paths.mjs` | Path resolution helpers for the setup wizard |
28
+ | `sync-constitution.ps1` | Regenerate `constitution.md` from `constitution.json` |
29
+ | `sync-dist.ps1` | Sync `dist/` to production deployment path |
30
+ | `append-agent-provenance.ps1` | Append agent provenance metadata to release artifacts |
31
+ | `Compare-TemplateSpec.ps1` | Diff current template against canonical spec |
32
+
33
+ ## Key entry point
34
+
35
+ ```pwsh
36
+ # Full release (bump → build → publish → tag)
37
+ pwsh -File scripts/build/Invoke-ReleaseWorkflow.ps1
38
+
39
+ # CI-compatible build with artifact verification
40
+ npm run build:ci
41
+ ```
@@ -0,0 +1,27 @@
1
+ /**
2
+ * setup-wizard-paths.mjs — Platform-standard per-user data directory resolver.
3
+ *
4
+ * Exported so the setup wizard and tests share a single source of truth.
5
+ *
6
+ * Convention (env-paths style; matches VS Code, npm, gh CLI):
7
+ * Windows: %LOCALAPPDATA%\index-server (default ~\AppData\Local\index-server)
8
+ * macOS: ~/Library/Application Support/index-server
9
+ * Linux: ${XDG_DATA_HOME:-~/.local/share}/index-server
10
+ */
11
+ import path from 'path';
12
+
13
+ const APP_NAME = 'index-server';
14
+
15
+ /** @param {NodeJS.Platform} [platform] @param {NodeJS.ProcessEnv} [env] */
16
+ export function defaultUserRoot(platform = process.platform, env = process.env) {
17
+ const home = env.HOME || env.USERPROFILE || '';
18
+ if (platform === 'win32') {
19
+ const base = env.LOCALAPPDATA || (home ? path.join(home, 'AppData', 'Local') : '');
20
+ return path.join(base, APP_NAME);
21
+ }
22
+ if (platform === 'darwin') {
23
+ return path.join(home, 'Library', 'Application Support', APP_NAME);
24
+ }
25
+ const base = env.XDG_DATA_HOME || path.join(home, '.local', 'share');
26
+ return path.join(base, APP_NAME);
27
+ }
@@ -15,7 +15,7 @@
15
15
  * npx @jagilber-org/index-server --setup
16
16
  * npm run setup
17
17
  * node scripts/build/setup-wizard.mjs
18
- * node scripts/build/setup-wizard.mjs --non-interactive --profile enhanced --root C:/mcp/index-server
18
+ * node scripts/build/setup-wizard.mjs --non-interactive --profile enhanced --root C:/.tools/index-server
19
19
  */
20
20
  import fs from 'fs';
21
21
  import path from 'path';
@@ -23,6 +23,7 @@ import { execFileSync } from 'child_process';
23
23
  import { fileURLToPath } from 'url';
24
24
  import { createRequire } from 'module';
25
25
  import { select, input, confirm, checkbox } from '@inquirer/prompts';
26
+ import { defaultUserRoot } from './setup-wizard-paths.mjs';
26
27
 
27
28
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
28
29
  const ROOT = path.resolve(__dirname, '..', '..');
@@ -33,17 +34,6 @@ function writeTextFile(filePath, content) {
33
34
  }
34
35
  const IS_WINDOWS = process.platform === 'win32';
35
36
 
36
- // Default install root for non-repo installs. Lives under the user profile so
37
- // neither admin/elevated rights nor a cluttered C:\ root are required, and the
38
- // runtime self-deploys cleanly there (args + cwd resolve under this root).
39
- function defaultUserRoot() {
40
- if (IS_WINDOWS) {
41
- const base = process.env.LOCALAPPDATA || process.env.APPDATA || process.env.USERPROFILE || process.cwd();
42
- return path.join(base, 'index-server');
43
- }
44
- const home = process.env.HOME || process.cwd();
45
- return path.join(home, '.local', 'share', 'index-server');
46
- }
47
37
  function parsePositiveTimeout(value, fallback) {
48
38
  const parsed = Number(value);
49
39
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
@@ -231,11 +221,10 @@ async function runInteractiveWizard() {
231
221
  default: 'default',
232
222
  });
233
223
 
234
- // Step 2: Root directory
235
- const defaultRoot = defaultUserRoot();
224
+ // Step 2: Root directory — defaults to per-user data dir (no admin rights, no hardcoded C:\ path)
236
225
  const root = path.resolve(await input({
237
226
  message: 'Base directory (all data paths resolve under this root)',
238
- default: defaultRoot,
227
+ default: defaultUserRoot(),
239
228
  }));
240
229
 
241
230
  // Step 3: Server name for mcp.json entry
@@ -648,12 +637,6 @@ Non-interactive mode:
648
637
  console.log(`\n✅ .env written to: ${envPath}`);
649
638
  }
650
639
 
651
- // ── Deploy runtime BEFORE config generation ─────────────────────────
652
- // resolveServerLaunch picks 'local' source (cwd = config.root) only when
653
- // <config.root>/dist/server/index-server.js exists. Deploy first so configs
654
- // are emitted with stable user-data-root paths instead of npm-global paths.
655
- await deployRuntime(config);
656
-
657
640
  // ── Multi-target config generation ──────────────────────────────────
658
641
  const configTargets = resolveConfigPaths(config);
659
642
 
@@ -711,6 +694,9 @@ Non-interactive mode:
711
694
  }
712
695
  }
713
696
 
697
+ // ── Deploy runtime if target root differs from package root ─────────
698
+ await deployRuntime(config);
699
+
714
700
  // ── Next steps ──────────────────────────────────────────────────────
715
701
  const proto = (config.profile === 'enhanced' || config.profile === 'experimental') ? 'https' : 'http';
716
702
  const launch = resolveServerLaunch(config);
@@ -0,0 +1,26 @@
1
+ # scripts/client
2
+
3
+ Client-side CLI tools and templates for connecting to a running Index Server via
4
+ MCP. These are the scripts users run against a deployed server; they do not
5
+ require the source tree to be built.
6
+
7
+ ## Scripts
8
+
9
+ | Script | Purpose |
10
+ |--------|---------|
11
+ | `index-server-client.ps1` | PowerShell MCP client: connect, call tools interactively |
12
+ | `index-server-client.sh` | Bash equivalent for Linux/macOS |
13
+ | `powershell-mcp-server.ps1` | Example PowerShell-hosted MCP server (reference implementation) |
14
+ | `powershell-mcp-template.ps1` | Template for building a custom PowerShell MCP server |
15
+
16
+ ## Quick start
17
+
18
+ ```pwsh
19
+ # Connect to a local dev server (stdio transport)
20
+ pwsh -File scripts/client/index-server-client.ps1
21
+
22
+ # Connect to a remote HTTP endpoint
23
+ pwsh -File scripts/client/index-server-client.ps1 -Uri http://localhost:9100
24
+ ```
25
+
26
+ > See `docs/client_scripts.md` and `docs/powershell_mcp_guide.md` for full usage.
@@ -0,0 +1,203 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Index Server REST client for subagents without MCP tool access.
4
+
5
+ .DESCRIPTION
6
+ Provides CRUD operations against the Index Server dashboard REST bridge
7
+ (POST /api/tools/:name). Works with both HTTP and HTTPS endpoints.
8
+ Returns structured JSON to stdout for machine consumption.
9
+
10
+ .PARAMETER BaseUrl
11
+ Server URL, e.g. http://localhost:8787 or https://localhost:8787
12
+
13
+ .PARAMETER Action
14
+ One of: search, get, list, add, remove, groom, health, track, hotset
15
+
16
+ .PARAMETER Id
17
+ Instruction ID (for get, remove, track)
18
+
19
+ .PARAMETER Keywords
20
+ Search keywords array (for search)
21
+
22
+ .PARAMETER Mode
23
+ Search mode: keyword, regex, semantic (default: keyword)
24
+
25
+ .PARAMETER Body
26
+ Instruction body text (for add)
27
+
28
+ .PARAMETER Title
29
+ Instruction title (for add)
30
+
31
+ .PARAMETER Priority
32
+ Instruction priority 1-100 (for add, default: 50)
33
+
34
+ .PARAMETER Signal
35
+ Usage signal: helpful, not-relevant, outdated, applied (for track)
36
+
37
+ .PARAMETER Overwrite
38
+ Allow overwriting existing instruction (for add)
39
+
40
+ .PARAMETER DryRun
41
+ Preview groom changes without writing (for groom)
42
+
43
+ .PARAMETER Limit
44
+ Max results (for search, list, hotset)
45
+
46
+ .PARAMETER ExpectId
47
+ Optional ID to prioritize in list results when validating CRUD flows
48
+
49
+ .PARAMETER SkipCertCheck
50
+ Skip TLS certificate validation (self-signed certs)
51
+
52
+ .EXAMPLE
53
+ .\index-server-client.ps1 -BaseUrl http://localhost:8787 -Action health
54
+ .\index-server-client.ps1 -BaseUrl http://localhost:8787 -Action search -Keywords deploy,release
55
+ .\index-server-client.ps1 -BaseUrl http://localhost:8787 -Action get -Id my-instruction-id
56
+ .\index-server-client.ps1 -BaseUrl http://localhost:8787 -Action list -Limit 20
57
+ .\index-server-client.ps1 -BaseUrl http://localhost:8787 -Action add -Id new-inst -Title "My Instruction" -Body "Content here"
58
+ .\index-server-client.ps1 -BaseUrl http://localhost:8787 -Action remove -Id old-inst
59
+ .\index-server-client.ps1 -BaseUrl http://localhost:8787 -Action track -Id some-inst -Signal helpful
60
+ .\index-server-client.ps1 -BaseUrl http://localhost:8787 -Action groom -DryRun
61
+ .\index-server-client.ps1 -BaseUrl https://localhost:8787 -Action health -SkipCertCheck
62
+ #>
63
+ [CmdletBinding()]
64
+ param(
65
+ [string]$BaseUrl = $env:INDEX_SERVER_URL,
66
+ [Parameter(Mandatory)]
67
+ [ValidateSet('search','get','list','add','remove','groom','health','track','hotset')]
68
+ [string]$Action,
69
+ [string]$Id,
70
+ [string[]]$Keywords,
71
+ [ValidateSet('keyword','regex','semantic')]
72
+ [string]$Mode = 'keyword',
73
+ [string]$Body,
74
+ [string]$Title,
75
+ [int]$Priority = 50,
76
+ [ValidateSet('helpful','not-relevant','outdated','applied')]
77
+ [string]$Signal,
78
+ [switch]$Overwrite,
79
+ [switch]$DryRun,
80
+ [int]$Limit = 50,
81
+ [string]$ExpectId,
82
+ [switch]$SkipCertCheck,
83
+ [string]$AdminKey = $env:INDEX_SERVER_ADMIN_API_KEY
84
+ )
85
+
86
+ $ErrorActionPreference = 'Stop'
87
+ if (-not $BaseUrl) {
88
+ $BaseUrl = 'http://localhost:8787'
89
+ }
90
+ $BaseUrl = $BaseUrl.TrimEnd('/')
91
+
92
+ function Invoke-Tool {
93
+ param([string]$Tool, [hashtable]$Params)
94
+ $uri = "$BaseUrl/api/tools/$Tool"
95
+ $jsonBody = $Params | ConvertTo-Json -Depth 10 -Compress
96
+ $splat = @{
97
+ Uri = $uri
98
+ Method = 'POST'
99
+ ContentType = 'application/json'
100
+ Body = $jsonBody
101
+ }
102
+ if ($AdminKey) {
103
+ $splat.Headers = @{ Authorization = "Bearer $AdminKey" }
104
+ }
105
+ if ($SkipCertCheck) {
106
+ if ($PSVersionTable.PSVersion.Major -ge 7) {
107
+ $splat.SkipCertificateCheck = $true
108
+ } else {
109
+ # PS 5.1 workaround
110
+ if (-not ([System.Management.Automation.PSTypeName]'TrustAll').Type) {
111
+ Add-Type @"
112
+ using System.Net;
113
+ using System.Net.Security;
114
+ using System.Security.Cryptography.X509Certificates;
115
+ public class TrustAll {
116
+ public static void Enable() {
117
+ ServicePointManager.ServerCertificateValidationCallback =
118
+ delegate { return true; };
119
+ }
120
+ }
121
+ "@
122
+ }
123
+ [TrustAll]::Enable()
124
+ }
125
+ }
126
+ try {
127
+ $resp = Invoke-RestMethod @splat
128
+ return @{ success = $true; result = $resp }
129
+ } catch {
130
+ $msg = $_.Exception.Message
131
+ $status = $null
132
+ if ($_.Exception.Response) {
133
+ $status = [int]$_.Exception.Response.StatusCode
134
+ try {
135
+ $reader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream())
136
+ $errBody = $reader.ReadToEnd() | ConvertFrom-Json
137
+ $msg = $errBody.error
138
+ } catch { }
139
+ }
140
+ return @{ success = $false; error = $msg; status = $status }
141
+ }
142
+ }
143
+
144
+ $output = switch ($Action) {
145
+ 'health' {
146
+ Invoke-Tool 'health_check' @{}
147
+ }
148
+ 'search' {
149
+ if (-not $Keywords -or $Keywords.Count -eq 0) {
150
+ @{ success = $false; error = 'Keywords required for search' }
151
+ } else {
152
+ Invoke-Tool 'index_search' @{ keywords = $Keywords; mode = $Mode; limit = $Limit }
153
+ }
154
+ }
155
+ 'get' {
156
+ if (-not $Id) { @{ success = $false; error = 'Id required for get' } }
157
+ else { Invoke-Tool 'index_dispatch' @{ action = 'get'; id = $Id } }
158
+ }
159
+ 'list' {
160
+ $p = @{ action = 'list'; limit = $Limit }
161
+ if ($ExpectId) { $p.expectId = $ExpectId }
162
+ Invoke-Tool 'index_dispatch' $p
163
+ }
164
+ 'add' {
165
+ if (-not $Id) { @{ success = $false; error = 'Id required for add' } }
166
+ elseif (-not $Body) { @{ success = $false; error = 'Body required for add' } }
167
+ else {
168
+ $entry = @{
169
+ id = $Id
170
+ title = if ($Title) { $Title } else { $Id }
171
+ body = $Body
172
+ priority = $Priority
173
+ audience = 'all'
174
+ requirement = 'optional'
175
+ categories = @('general')
176
+ contentType = 'instruction'
177
+ }
178
+ $params = @{ entry = $entry; lax = $true }
179
+ if ($Overwrite) { $params.overwrite = $true }
180
+ Invoke-Tool 'index_add' $params
181
+ }
182
+ }
183
+ 'remove' {
184
+ if (-not $Id) { @{ success = $false; error = 'Id required for remove' } }
185
+ else { Invoke-Tool 'index_remove' @{ ids = @($Id) } }
186
+ }
187
+ 'track' {
188
+ if (-not $Id) { @{ success = $false; error = 'Id required for track' } }
189
+ else {
190
+ $p = @{ id = $Id }
191
+ if ($Signal) { $p.signal = $Signal }
192
+ Invoke-Tool 'usage_track' $p
193
+ }
194
+ }
195
+ 'hotset' {
196
+ Invoke-Tool 'usage_hotset' @{ limit = $Limit }
197
+ }
198
+ 'groom' {
199
+ Invoke-Tool 'index_groom' @{ mode = @{ dryRun = [bool]$DryRun } }
200
+ }
201
+ }
202
+
203
+ $output | ConvertTo-Json -Depth 10
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env bash
2
+ # index-server-client.sh -- Index Server REST client for subagents without MCP tool access
3
+ # Provides CRUD operations via the dashboard REST bridge (POST /api/tools/:name)
4
+ # Works with both HTTP and HTTPS. Returns structured JSON to stdout.
5
+ #
6
+ # Usage:
7
+ # ./index-server-client.sh health
8
+ # ./index-server-client.sh search "deploy release" [keyword|regex|semantic] [limit]
9
+ # ./index-server-client.sh get <id>
10
+ # ./index-server-client.sh list [limit]
11
+ # ./index-server-client.sh add <id> <title> <body> [priority] [--overwrite]
12
+ # ./index-server-client.sh remove <id>
13
+ # ./index-server-client.sh track <id> [helpful|not-relevant|outdated|applied]
14
+ # ./index-server-client.sh hotset [limit]
15
+ # ./index-server-client.sh groom [--dry-run]
16
+ #
17
+ # Env: INDEX_SERVER_URL (default: http://localhost:8787)
18
+ # INDEX_SERVER_ADMIN_API_KEY to authenticate with Bearer token
19
+ # INDEX_SERVER_SKIP_CERT=1 to skip TLS cert validation (self-signed)
20
+
21
+ set -euo pipefail
22
+
23
+ BASE_URL="${INDEX_SERVER_URL:-http://localhost:8787}"
24
+ BASE_URL="${BASE_URL%/}"
25
+ ADMIN_KEY="${INDEX_SERVER_ADMIN_API_KEY:-}"
26
+
27
+ CURL_OPTS=(-s -S --fail-with-body -H "Content-Type: application/json")
28
+ if [ -n "$ADMIN_KEY" ]; then
29
+ CURL_OPTS+=(-H "Authorization: Bearer ${ADMIN_KEY}")
30
+ fi
31
+ if [ "${INDEX_SERVER_SKIP_CERT:-}" = "1" ]; then
32
+ CURL_OPTS+=(-k)
33
+ fi
34
+
35
+ call_tool() {
36
+ local tool="$1"
37
+ local body="$2"
38
+ local url="${BASE_URL}/api/tools/${tool}"
39
+ local http_code resp
40
+ resp=$(curl "${CURL_OPTS[@]}" -w "\n%{http_code}" -X POST -d "$body" "$url" 2>&1) || true
41
+ http_code=$(echo "$resp" | tail -1)
42
+ local json_body
43
+ json_body=$(echo "$resp" | sed '$d')
44
+ if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ] 2>/dev/null; then
45
+ echo "{\"success\":true,\"status\":${http_code},\"result\":${json_body:-null}}"
46
+ else
47
+ echo "{\"success\":false,\"status\":${http_code:-0},\"error\":${json_body:-\"request failed\"}}"
48
+ fi
49
+ }
50
+
51
+ ACTION="${1:-}"
52
+ shift || true
53
+
54
+ case "$ACTION" in
55
+ health)
56
+ call_tool "health_check" "{}"
57
+ ;;
58
+ search)
59
+ terms="${1:-}"
60
+ mode="${2:-keyword}"
61
+ limit="${3:-50}"
62
+ if [ -z "$terms" ]; then
63
+ echo '{"success":false,"error":"keywords required for search"}'
64
+ exit 1
65
+ fi
66
+ # Convert space-separated terms to JSON array
67
+ kw_json=$(echo "$terms" | tr ' ' '\n' | sed 's/.*/"&"/' | paste -sd, | sed 's/^/[/;s/$/]/')
68
+ call_tool "index_search" "{\"keywords\":${kw_json},\"mode\":\"${mode}\",\"limit\":${limit}}"
69
+ ;;
70
+ get)
71
+ id="${1:-}"
72
+ if [ -z "$id" ]; then
73
+ echo '{"success":false,"error":"id required for get"}'
74
+ exit 1
75
+ fi
76
+ call_tool "index_dispatch" "{\"action\":\"get\",\"id\":\"${id}\"}"
77
+ ;;
78
+ list)
79
+ limit="${1:-50}"
80
+ call_tool "index_dispatch" "{\"action\":\"list\",\"limit\":${limit}}"
81
+ ;;
82
+ add)
83
+ id="${1:-}"
84
+ title="${2:-}"
85
+ body="${3:-}"
86
+ priority="${4:-50}"
87
+ overwrite="false"
88
+ for arg in "$@"; do [ "$arg" = "--overwrite" ] && overwrite="true"; done
89
+ if [ -z "$id" ] || [ -z "$body" ]; then
90
+ echo '{"success":false,"error":"id and body required for add"}'
91
+ exit 1
92
+ fi
93
+ [ -z "$title" ] && title="$id"
94
+ # Escape body for JSON (newlines, quotes, backslashes)
95
+ esc_body=$(printf '%s' "$body" | python3 -c 'import json,sys;print(json.dumps(sys.stdin.read()))' 2>/dev/null || printf '%s' "$body" | sed 's/\\/\\\\/g;s/"/\\"/g' | tr '\n' ' ')
96
+ esc_title=$(printf '%s' "$title" | sed 's/\\/\\\\/g;s/"/\\"/g')
97
+ call_tool "index_add" "{\"entry\":{\"id\":\"${id}\",\"title\":\"${esc_title}\",\"body\":${esc_body},\"priority\":${priority},\"audience\":\"all\",\"requirement\":\"optional\",\"categories\":[\"general\"],\"contentType\":\"instruction\"},\"lax\":true,\"overwrite\":${overwrite}}"
98
+ ;;
99
+ remove)
100
+ id="${1:-}"
101
+ if [ -z "$id" ]; then
102
+ echo '{"success":false,"error":"id required for remove"}'
103
+ exit 1
104
+ fi
105
+ call_tool "index_remove" "{\"ids\":[\"${id}\"]}"
106
+ ;;
107
+ track)
108
+ id="${1:-}"
109
+ signal="${2:-}"
110
+ if [ -z "$id" ]; then
111
+ echo '{"success":false,"error":"id required for track"}'
112
+ exit 1
113
+ fi
114
+ if [ -n "$signal" ]; then
115
+ call_tool "usage_track" "{\"id\":\"${id}\",\"signal\":\"${signal}\"}"
116
+ else
117
+ call_tool "usage_track" "{\"id\":\"${id}\"}"
118
+ fi
119
+ ;;
120
+ hotset)
121
+ limit="${1:-10}"
122
+ call_tool "usage_hotset" "{\"limit\":${limit}}"
123
+ ;;
124
+ groom)
125
+ dry="false"
126
+ for arg in "$@"; do [ "$arg" = "--dry-run" ] && dry="true"; done
127
+ call_tool "index_groom" "{\"mode\":{\"dryRun\":${dry}}}"
128
+ ;;
129
+ *)
130
+ cat <<'EOF'
131
+ {"success":false,"error":"unknown action","usage":{
132
+ "actions":["health","search","get","list","add","remove","track","hotset","groom"],
133
+ "examples":[
134
+ "index-server-client.sh health",
135
+ "index-server-client.sh search 'deploy release' semantic 10",
136
+ "index-server-client.sh get my-instruction-id",
137
+ "index-server-client.sh list 20",
138
+ "index-server-client.sh add my-id 'My Title' 'Body content' 50",
139
+ "index-server-client.sh remove my-id",
140
+ "index-server-client.sh track my-id helpful",
141
+ "index-server-client.sh hotset 10",
142
+ "index-server-client.sh groom --dry-run"
143
+ ],
144
+ "env":["INDEX_SERVER_URL=http://localhost:8787","INDEX_SERVER_SKIP_CERT=1"]
145
+ }}
146
+ EOF
147
+ exit 1
148
+ ;;
149
+ esac