aiwg 2026.7.16 → 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 +1 -1
- package/README.md +25 -4
- package/dist/src/agents/grounding/knowledge-base.js +3 -2
- package/dist/src/api/index.d.ts +10 -0
- package/dist/src/api/index.js +10 -0
- package/dist/src/artifacts/audit/cli.js +2 -1
- package/dist/src/artifacts/browser-export.js +3 -0
- package/dist/src/artifacts/cli.js +118 -3
- package/dist/src/artifacts/fortemi-core-query-adapter.js +89 -4
- package/dist/src/artifacts/fortemi-core-sync.js +2 -2
- package/dist/src/artifacts/hybrid-query.js +2 -1
- package/dist/src/artifacts/index-builder.js +46 -20
- package/dist/src/artifacts/index-reader.js +5 -5
- package/dist/src/artifacts/index-status.js +2 -2
- package/dist/src/artifacts/legacy-index-migration.js +3 -3
- 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 +232 -25
- package/dist/src/artifacts/stats.js +3 -3
- package/dist/src/artifacts/types.js +34 -6
- package/dist/src/artifacts/watcher.js +6 -4
- package/dist/src/cli/cli-extension-loader.js +3 -2
- package/dist/src/cli/command-log.js +6 -2
- package/dist/src/cli/find-package-root.js +4 -4
- package/dist/src/cli/handlers/artifacts.js +76 -0
- package/dist/src/cli/handlers/best-practices-audit.js +2 -1
- package/dist/src/cli/handlers/execution-mode.js +3 -3
- package/dist/src/cli/handlers/help.js +2 -0
- package/dist/src/cli/handlers/index.js +4 -2
- package/dist/src/cli/handlers/init.js +2 -2
- package/dist/src/cli/handlers/local-executor.js +2 -2
- package/dist/src/cli/handlers/ralph-launcher.js +3 -2
- package/dist/src/cli/handlers/regenerate.js +4 -3
- package/dist/src/cli/handlers/serve.js +6 -5
- package/dist/src/cli/handlers/setup.js +2 -2
- package/dist/src/cli/handlers/subcommands.js +6 -4
- package/dist/src/cli/handlers/team.js +3 -2
- package/dist/src/cli/handlers/use.js +123 -2
- package/dist/src/cli/handlers/utilities.js +2 -1
- package/dist/src/cli/help-generator.js +1 -0
- package/dist/src/cli/scope-resolver.js +31 -1
- package/dist/src/cli/skill-usage.js +6 -2
- package/dist/src/cli/workspace-signals.js +9 -6
- package/dist/src/community/milestones.js +2 -1
- package/dist/src/config/aiwg-config.js +10 -7
- package/dist/src/config/project-artifacts.js +76 -0
- package/dist/src/contributors/discover.js +3 -2
- package/dist/src/extensions/commands/definitions.js +33 -5
- package/dist/src/extensions/project-local-discovery.js +75 -85
- package/dist/src/extensions/project-local-doctor.js +14 -6
- package/dist/src/extensions/project-local-paths.js +121 -0
- package/dist/src/extensions/project-local-promote.js +1 -1
- package/dist/src/extensions/project-local-scaffold.js +3 -8
- package/dist/src/extensions/project-quickref.js +6 -7
- package/dist/src/issues/local.js +2 -1
- package/dist/src/lint/loader.js +2 -1
- package/dist/src/mcp/helpers.mjs +58 -3
- package/dist/src/mcp/server.mjs +3 -28
- package/dist/src/memory/project-registry.js +6 -5
- package/dist/src/models/provider-policy.js +1 -0
- package/dist/src/ops/registry.js +2 -1
- package/dist/src/policy/authorization.js +3 -2
- package/dist/src/policy/repo-access.js +5 -4
- package/dist/src/providers/capability-matrix.js +6 -3
- package/dist/src/release/plan.js +2 -1
- 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/cli.js +3 -2
- package/dist/src/serve/executor-registry.js +2 -1
- package/dist/src/serve/sandbox-registry.js +2 -1
- package/dist/src/smiths/context-pipeline/finalization.js +10 -2
- package/dist/src/smiths/context-pipeline/workspace-context.js +31 -16
- package/dist/src/storage/config.js +6 -6
- package/dist/src/writing/scoring-config-loader.js +2 -1
- package/docs/.public/blog/2026-07-aiwg-workflow-portability-cli.svg +71 -0
- package/docs/.public/blog/2026-07-aiwg-workflow-portability-hero.png +0 -0
- package/docs/CLI_USAGE.md +37 -0
- package/docs/_manifest.json +35 -1
- package/docs/agentic-install-runbook.md +18 -5
- package/docs/blog/2026-07-aiwg-workflow-portability.md +143 -0
- package/docs/blog/_manifest.json +1 -0
- package/docs/cli-reference.md +388 -302
- package/docs/cockpit/README.md +3 -3
- package/docs/cockpit/architecture.md +10 -10
- package/docs/cockpit/bridge-api.md +16 -4
- package/docs/cockpit/daily-operator-gate.md +155 -0
- package/docs/cockpit/development.md +1 -0
- package/docs/cockpit/recovery.md +7 -1
- package/docs/cockpit/sessions.md +2 -2
- package/docs/cockpit/trust-and-security.md +20 -2
- package/docs/configuration/aiwg-config.md +24 -3
- package/docs/contributing/ci-cd-secrets.md +3 -0
- package/docs/contributing/publishing-blog-posts.md +18 -0
- package/docs/contributing/versioning.md +40 -14
- package/docs/customization/project-local-lifecycle.md +29 -3
- package/docs/customization/project-quickrefs.md +6 -3
- package/docs/customization/user-scope-deployment.md +29 -1
- package/docs/development/_manifest.json +1 -0
- package/docs/development/aiwg-development-guide.md +15 -4
- package/docs/development/private-aiwg-corpus.md +73 -0
- package/docs/fortemi-core-prebuilt-indices.md +27 -19
- package/docs/install/_manifest.json +9 -0
- package/docs/install/web-backed-resources.md +138 -0
- package/docs/integrations/fortemi-index-export.md +47 -0
- package/docs/releases/v2026.7.17-announcement.md +73 -0
- package/docs/releases/v2026.7.18-announcement.md +64 -0
- package/package.json +21 -2
- package/prebuilt/fortemi-core/framework/aiwg-fortemi-index-v2.json +1 -1
- package/prebuilt/fortemi-core/framework/manifest.json +2 -2
- package/tools/cli/check-install-size.mjs +2 -2
- package/tools/docs/promote-scheduled-posts.mjs +158 -0
- package/tools/docs/write-blog-static-pages.mjs +108 -0
- package/tools/workspace/check-marketplace-version.mjs +24 -6
package/LICENSE
CHANGED
|
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -6,13 +6,26 @@
|
|
|
6
6
|
|
|
7
7
|
**Multi-agent AI framework for Claude Code, Copilot, Cursor, Warp, and 6 more platforms**
|
|
8
8
|
|
|
9
|
-
200+ agents,
|
|
9
|
+
200+ agents, 109+ CLI commands, 400+ deployable agent/skill/command/rule artifacts, 8 core frameworks, 27 addons, and a training marketplace package. SDLC workflows, digital forensics, research management, marketing operations, media curation, ops infrastructure, knowledge base, and fine-tuning dataset curation — all deployable with one command.
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm i -g aiwg #
|
|
12
|
+
npm i -g aiwg # full CLI and local resource corpus
|
|
13
13
|
aiwg use sdlc # deploy SDLC framework
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
For a smaller CLI install that resolves signed, versioned resources from the
|
|
17
|
+
release host:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm i -g @aiwg/cli
|
|
21
|
+
aiwg discover "architecture evolution"
|
|
22
|
+
aiwg show skill architecture-evolution
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
See [Web-Backed AIWG Resources](docs/install/web-backed-resources.md) for source
|
|
26
|
+
selection, exact-version overrides, cache verification, offline use, and the
|
|
27
|
+
current framework-graph constraints.
|
|
28
|
+
|
|
16
29
|
Then ask your AI assistant to set up the project for AIWG. The agent-led setup
|
|
17
30
|
conversation should establish remotes, issue storage, delivery behavior,
|
|
18
31
|
signing policy, and provider choices; the assistant may call `aiwg setup project`
|
|
@@ -111,10 +124,18 @@ Around that core, AIWG ships agent-facing utilities for things the base platform
|
|
|
111
124
|
|
|
112
125
|
### Project scope (recommended) vs user scope (global)
|
|
113
126
|
|
|
114
|
-
`aiwg use`
|
|
127
|
+
`aiwg use` supports project deployments, additive user mirrors, and a
|
|
128
|
+
user-global bootstrap:
|
|
115
129
|
|
|
116
130
|
- **Project scope** — default. Run `aiwg use sdlc` from a project root and the artifacts land in `./.claude/agents/`, `./.claude/skills/`, etc. One project's agent set never bleeds into another's session. **This is the recommended default for most use cases.**
|
|
117
|
-
- **User scope (
|
|
131
|
+
- **User scope (additive mirror)** — `aiwg use sdlc --scope user` keeps the
|
|
132
|
+
project deployment and mirrors it to `~/.claude/agents/`,
|
|
133
|
+
`~/.claude/skills/`, etc.
|
|
134
|
+
- **Global bootstrap** — `aiwg use sdlc --provider claude --global` installs
|
|
135
|
+
framework and kernel assets in native user-level paths while leaving only
|
|
136
|
+
lightweight context and provider bootstrap files in the current project.
|
|
137
|
+
Use `aiwg regenerate --provider <name>` to wire additional projects without
|
|
138
|
+
deploying their own skill copies.
|
|
118
139
|
|
|
119
140
|
The trade-off is real: when the same agent set loads into every session, context from one project can bleed into reasoning about another. Research (REF-720, *Lost in Multi-Turn Conversation*, MSR/Salesforce 2025) measured a 39% capability drop when this happens. The non-blocking project-isolation warning surfaces the trade-off at deploy time so the scope choice is informed. Neither scope is wrong; pick the one that fits the workflow.
|
|
120
141
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { promises as fs } from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
|
+
import { projectAiwgPath } from '../../config/project-artifacts.js';
|
|
9
10
|
// ============================================================================
|
|
10
11
|
// KnowledgeStore
|
|
11
12
|
// ============================================================================
|
|
@@ -20,7 +21,7 @@ export class KnowledgeStore {
|
|
|
20
21
|
* Load knowledge bases from .aiwg/knowledge/
|
|
21
22
|
*/
|
|
22
23
|
async load() {
|
|
23
|
-
const knowledgeDir =
|
|
24
|
+
const knowledgeDir = projectAiwgPath(this.projectPath, 'knowledge');
|
|
24
25
|
try {
|
|
25
26
|
const domains = await fs.readdir(knowledgeDir);
|
|
26
27
|
for (const domain of domains) {
|
|
@@ -73,7 +74,7 @@ export class KnowledgeStore {
|
|
|
73
74
|
existing.entries.push(...entries);
|
|
74
75
|
this.bases.set(domain, existing);
|
|
75
76
|
// Persist
|
|
76
|
-
const domainDir =
|
|
77
|
+
const domainDir = projectAiwgPath(this.projectPath, 'knowledge', domain);
|
|
77
78
|
await fs.mkdir(domainDir, { recursive: true });
|
|
78
79
|
const outFile = path.join(domainDir, path.basename(filePath));
|
|
79
80
|
await fs.copyFile(filePath, outFile);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported programmatic entry points for the installed AIWG package.
|
|
3
|
+
*
|
|
4
|
+
* `run()` dispatches through the same router used by `bin/aiwg.mjs`. Resource
|
|
5
|
+
* helpers expose the signed web-release contract without requiring callers to
|
|
6
|
+
* import private `dist/` paths.
|
|
7
|
+
*/
|
|
8
|
+
export { run } from '../cli/router.js';
|
|
9
|
+
export * from '../resources/index.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported programmatic entry points for the installed AIWG package.
|
|
3
|
+
*
|
|
4
|
+
* `run()` dispatches through the same router used by `bin/aiwg.mjs`. Resource
|
|
5
|
+
* helpers expose the signed web-release contract without requiring callers to
|
|
6
|
+
* import private `dist/` paths.
|
|
7
|
+
*/
|
|
8
|
+
export { run } from '../cli/router.js';
|
|
9
|
+
export * from '../resources/index.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -22,6 +22,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
22
22
|
import { join } from 'node:path';
|
|
23
23
|
import { computeContentHash, list as listEnrichments, resolveSemanticRoot, get as getEnrichment, } from '../enrichment/store.js';
|
|
24
24
|
import { validateEnrichmentOutput } from '../enrichment/prompt.js';
|
|
25
|
+
import { projectAiwgPath } from '../../config/project-artifacts.js';
|
|
25
26
|
import { detectDrift } from './drift.js';
|
|
26
27
|
import { DEFAULT_THRESHOLDS } from './types.js';
|
|
27
28
|
export async function main(args) {
|
|
@@ -115,7 +116,7 @@ export async function main(args) {
|
|
|
115
116
|
}
|
|
116
117
|
function loadThresholds() {
|
|
117
118
|
// Try .aiwg/index/audit.config.yaml → fall back to defaults
|
|
118
|
-
const path =
|
|
119
|
+
const path = projectAiwgPath(process.cwd(), 'index', 'audit.config.yaml');
|
|
119
120
|
if (!existsSync(path))
|
|
120
121
|
return DEFAULT_THRESHOLDS;
|
|
121
122
|
try {
|
|
@@ -505,6 +505,9 @@ function recordForEntry(cwd, entry, graphName, dependencyGraph, privacy, schemaV
|
|
|
505
505
|
embeddings: [],
|
|
506
506
|
skos_concepts: skos?.concepts ?? [],
|
|
507
507
|
skos_relations: skos?.relations ?? [],
|
|
508
|
+
...(entry.operationalState
|
|
509
|
+
? { operational_state: entry.operationalState }
|
|
510
|
+
: {}),
|
|
508
511
|
}
|
|
509
512
|
: {}),
|
|
510
513
|
updated_at: entry.updated,
|
|
@@ -16,8 +16,40 @@
|
|
|
16
16
|
* @source @src/cli/handlers/subcommands.ts
|
|
17
17
|
* @tests @test/unit/artifacts/cli.test.ts
|
|
18
18
|
*/
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
19
22
|
import { GRAPH_CONFIGS, OPERATIONAL_DISCOVERY_TYPES, OPERATIONAL_SHOW_TYPES, isOperationalShowType, loadUserGraphConfigs, loadGlobalGraphConfigs, orderedGraphEntries, } from './types.js';
|
|
20
23
|
import { SUPPORTED_VIEWS } from './corpus-views/renderers.js';
|
|
24
|
+
import { parseResourceSelector, readVerifiedRegularFile, } from '../resources/web-release.js';
|
|
25
|
+
import { findPackageRoot } from '../cli/find-package-root.js';
|
|
26
|
+
const MAX_RESOURCE_TRUST_ROOT_BYTES = 64 * 1024;
|
|
27
|
+
function webReleaseOptionsFromEnvironment() {
|
|
28
|
+
const baseUrl = process.env.AIWG_RESOURCE_BASE_URL;
|
|
29
|
+
const cacheRoot = process.env.AIWG_RESOURCE_CACHE_ROOT;
|
|
30
|
+
const trustRootFile = process.env.AIWG_RESOURCE_TRUST_ROOT_FILE;
|
|
31
|
+
let publicKeyPem;
|
|
32
|
+
if (trustRootFile !== undefined) {
|
|
33
|
+
if (trustRootFile.trim().length === 0) {
|
|
34
|
+
throw new Error('AIWG_RESOURCE_TRUST_ROOT_FILE must name a non-empty public PEM file');
|
|
35
|
+
}
|
|
36
|
+
publicKeyPem = readVerifiedRegularFile(path.resolve(trustRootFile), {
|
|
37
|
+
label: 'AIWG_RESOURCE_TRUST_ROOT_FILE public PEM',
|
|
38
|
+
maxBytes: MAX_RESOURCE_TRUST_ROOT_BYTES,
|
|
39
|
+
});
|
|
40
|
+
if (publicKeyPem.toString('utf8').trim().length === 0) {
|
|
41
|
+
throw new Error('AIWG_RESOURCE_TRUST_ROOT_FILE public PEM is empty');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
...(baseUrl === undefined ? {} : { baseUrl }),
|
|
46
|
+
...(cacheRoot === undefined ? {} : { cacheRoot }),
|
|
47
|
+
...(publicKeyPem === undefined ? {} : { publicKeyPem }),
|
|
48
|
+
...(process.env.AIWG_RESOURCE_ALLOW_INSECURE_LOOPBACK_HTTP === '1'
|
|
49
|
+
? { allowInsecureLoopbackHttp: true }
|
|
50
|
+
: {}),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
21
53
|
/** Parse --graph flag from args, returns undefined for "all graphs" */
|
|
22
54
|
function parseGraphFlag(args) {
|
|
23
55
|
const idx = args.indexOf('--graph');
|
|
@@ -53,6 +85,67 @@ function parseBackendFlag(args) {
|
|
|
53
85
|
function parseSearchBackendFlag(args) {
|
|
54
86
|
return parseBackendFlag(args) ?? 'fortemi-core';
|
|
55
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* The full `aiwg` package owns a local corpus, while `@aiwg/cli` intentionally
|
|
90
|
+
* ships only executable code and embedded runtime metadata. Select web
|
|
91
|
+
* resources automatically for the lightweight distribution so a normal
|
|
92
|
+
* `aiwg discover` / `aiwg show` invocation works immediately after install.
|
|
93
|
+
*
|
|
94
|
+
* Resolve from this module rather than process.argv so the exported API gets
|
|
95
|
+
* the same package-aware default as the npm binary.
|
|
96
|
+
*/
|
|
97
|
+
function defaultResourceSource() {
|
|
98
|
+
const packageRoot = findPackageRoot(path.dirname(fileURLToPath(import.meta.url)));
|
|
99
|
+
if (!packageRoot)
|
|
100
|
+
return 'local';
|
|
101
|
+
try {
|
|
102
|
+
const manifest = JSON.parse(readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
103
|
+
return manifest.name === '@aiwg/cli' ? 'web' : 'local';
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return 'local';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function parseResourceSourceFlag(args) {
|
|
110
|
+
const indices = args
|
|
111
|
+
.map((arg, index) => arg === '--resource-source' ? index : -1)
|
|
112
|
+
.filter((index) => index >= 0);
|
|
113
|
+
if (indices.length > 1) {
|
|
114
|
+
console.error('Error: --resource-source may be specified only once');
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
if (indices.length === 0)
|
|
118
|
+
return defaultResourceSource();
|
|
119
|
+
const value = args[indices[0] + 1];
|
|
120
|
+
if (value === 'local' || value === 'web' || value === 'auto')
|
|
121
|
+
return value;
|
|
122
|
+
console.error('Error: --resource-source must be local, web, or auto');
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
function parseAiwgVersionFlag(args) {
|
|
126
|
+
const indices = args
|
|
127
|
+
.map((arg, index) => arg === '--aiwg-version' ? index : -1)
|
|
128
|
+
.filter((index) => index >= 0);
|
|
129
|
+
if (indices.length > 1) {
|
|
130
|
+
console.error('Error: --aiwg-version may be specified only once');
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
if (indices.length === 0)
|
|
134
|
+
return undefined;
|
|
135
|
+
const value = args[indices[0] + 1];
|
|
136
|
+
if (!value || value.startsWith('--')) {
|
|
137
|
+
console.error('Error: --aiwg-version requires an exact calendar-semver version or channel name');
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
parseResourceSelector(value);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
56
149
|
function firstPositionalArg(args, valueFlags) {
|
|
57
150
|
const skip = new Set();
|
|
58
151
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -1215,13 +1308,14 @@ async function handleDiscover(args) {
|
|
|
1215
1308
|
if (!phrase) {
|
|
1216
1309
|
console.error('Error: aiwg index discover requires a search phrase');
|
|
1217
1310
|
console.log('');
|
|
1218
|
-
console.log('Usage: aiwg index discover "<phrase>" [--type <kinds>] [--limit N] [--json|--format json|text] [--pretty|--compact] [--graph <name>] [--backend local|fortemi-core]');
|
|
1311
|
+
console.log('Usage: aiwg index discover "<phrase>" [--type <kinds>] [--limit N] [--json|--format json|text] [--pretty|--compact] [--graph <name>] [--backend local|fortemi-core] [--resource-source local|web|auto] [--aiwg-version <exact-or-channel>] [--offline]');
|
|
1219
1312
|
console.log('');
|
|
1220
1313
|
console.log('Examples:');
|
|
1221
1314
|
console.log(' aiwg index discover "create intake"');
|
|
1222
1315
|
console.log(' aiwg index discover "deploy production" --limit 5');
|
|
1223
1316
|
console.log(` aiwg index discover "audit security" --type ${OPERATIONAL_DISCOVERY_TYPES.join(',')}`);
|
|
1224
1317
|
console.log(' aiwg index discover "intake" --format json --pretty');
|
|
1318
|
+
console.log(' aiwg discover "intake" --resource-source web --aiwg-version stable');
|
|
1225
1319
|
process.exit(1);
|
|
1226
1320
|
}
|
|
1227
1321
|
const typeValue = parseFlagValue(flags, '--type', 'Error: --type requires a value');
|
|
@@ -1244,6 +1338,12 @@ async function handleDiscover(args) {
|
|
|
1244
1338
|
const jsonPretty = !flags.includes('--compact');
|
|
1245
1339
|
const graph = parseGraphFlag(flags);
|
|
1246
1340
|
const backend = parseSearchBackendFlag(flags);
|
|
1341
|
+
const resourceSource = parseResourceSourceFlag(flags);
|
|
1342
|
+
const aiwgVersion = parseAiwgVersionFlag(flags);
|
|
1343
|
+
const offline = flags.includes('--offline');
|
|
1344
|
+
const webReleaseOptions = resourceSource === 'local'
|
|
1345
|
+
? undefined
|
|
1346
|
+
: webReleaseOptionsFromEnvironment();
|
|
1247
1347
|
await discoverCapability(cwd, {
|
|
1248
1348
|
phrase,
|
|
1249
1349
|
typeFilter,
|
|
@@ -1252,6 +1352,10 @@ async function handleDiscover(args) {
|
|
|
1252
1352
|
jsonPretty,
|
|
1253
1353
|
graph,
|
|
1254
1354
|
backend,
|
|
1355
|
+
resourceSource,
|
|
1356
|
+
aiwgVersion,
|
|
1357
|
+
offline,
|
|
1358
|
+
webReleaseOptions,
|
|
1255
1359
|
includePaths: false,
|
|
1256
1360
|
});
|
|
1257
1361
|
}
|
|
@@ -1284,8 +1388,8 @@ async function handleShow(args) {
|
|
|
1284
1388
|
}
|
|
1285
1389
|
const HELP_TEXT = [
|
|
1286
1390
|
'',
|
|
1287
|
-
'Usage: aiwg show <type> <name> [--json] [--first] [--graph <name>] [--backend local|fortemi-core]',
|
|
1288
|
-
' aiwg show metadata <id-or-name-or-path> [--json] [--first] [--graph <name>] [--backend local|fortemi-core]',
|
|
1391
|
+
'Usage: aiwg show <type> <name> [--json] [--first] [--graph <name>] [--backend local|fortemi-core] [--resource-source local|web|auto] [--aiwg-version <exact-or-channel>] [--offline]',
|
|
1392
|
+
' aiwg show metadata <id-or-name-or-path> [--json] [--first] [--graph <name>] [--backend local|fortemi-core] [--resource-source local|web|auto] [--aiwg-version <exact-or-channel>] [--offline]',
|
|
1289
1393
|
' aiwg index show <type> <name> ...',
|
|
1290
1394
|
'',
|
|
1291
1395
|
`Types: ${OPERATIONAL_SHOW_TYPES.join(' | ')}`,
|
|
@@ -1299,6 +1403,7 @@ async function handleShow(args) {
|
|
|
1299
1403
|
' aiwg show metadata aiwg:skill:4840fa441622f676 --json',
|
|
1300
1404
|
' aiwg show agent aiwg-steward',
|
|
1301
1405
|
' aiwg show command discover',
|
|
1406
|
+
' aiwg show skill intake-wizard --resource-source web --aiwg-version stable',
|
|
1302
1407
|
'',
|
|
1303
1408
|
'Tip: use `aiwg discover "<phrase>" --json` first to find the stable id.',
|
|
1304
1409
|
].join('\n');
|
|
@@ -1355,6 +1460,12 @@ async function handleShow(args) {
|
|
|
1355
1460
|
}
|
|
1356
1461
|
const graph = parseGraphFlag(flags);
|
|
1357
1462
|
const backend = parseSearchBackendFlag(flags);
|
|
1463
|
+
const resourceSource = parseResourceSourceFlag(flags);
|
|
1464
|
+
const aiwgVersion = parseAiwgVersionFlag(flags);
|
|
1465
|
+
const offline = flags.includes('--offline');
|
|
1466
|
+
const webReleaseOptions = resourceSource === 'local'
|
|
1467
|
+
? undefined
|
|
1468
|
+
: webReleaseOptionsFromEnvironment();
|
|
1358
1469
|
const params = {
|
|
1359
1470
|
name,
|
|
1360
1471
|
typeFilter: type ? [type] : undefined,
|
|
@@ -1362,6 +1473,10 @@ async function handleShow(args) {
|
|
|
1362
1473
|
first,
|
|
1363
1474
|
graph,
|
|
1364
1475
|
backend,
|
|
1476
|
+
resourceSource,
|
|
1477
|
+
aiwgVersion,
|
|
1478
|
+
offline,
|
|
1479
|
+
webReleaseOptions,
|
|
1365
1480
|
};
|
|
1366
1481
|
if (metadataMode)
|
|
1367
1482
|
await showMetadata(cwd, params);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { bm25Rank } from "./fulltext.js";
|
|
3
4
|
import { getFortemiCorePrebuiltStatus, getFortemiCoreSyncStatus } from "./fortemi-core-sync.js";
|
|
5
|
+
import { operationalStateQueryProjection, } from "./operational-state.js";
|
|
4
6
|
function tokenize(text) {
|
|
5
7
|
return text
|
|
6
8
|
.toLowerCase()
|
|
@@ -59,6 +61,9 @@ function recordToStaticResult(record, score, matched) {
|
|
|
59
61
|
summary: record.summary ?? record.search?.summary,
|
|
60
62
|
score: Math.round(score * 1000) / 1000,
|
|
61
63
|
matched,
|
|
64
|
+
...(record.operational_state
|
|
65
|
+
? { operational_state: operationalStateQueryProjection(record.operational_state) }
|
|
66
|
+
: {}),
|
|
62
67
|
};
|
|
63
68
|
}
|
|
64
69
|
function recordBody(record) {
|
|
@@ -85,7 +90,83 @@ function matchesPath(recordPath, pattern) {
|
|
|
85
90
|
}
|
|
86
91
|
return recordPath.includes(pattern);
|
|
87
92
|
}
|
|
88
|
-
|
|
93
|
+
function readVerifiedRegularFile(pathname, expectedSize, expectedSha256, label) {
|
|
94
|
+
if (!Number.isSafeInteger(expectedSize) || expectedSize < 0) {
|
|
95
|
+
throw new Error(`${label} has an invalid signed size`);
|
|
96
|
+
}
|
|
97
|
+
const pathStat = fs.lstatSync(pathname);
|
|
98
|
+
if (pathStat.isSymbolicLink() || !pathStat.isFile()) {
|
|
99
|
+
throw new Error(`${label} must be a regular file and may not be a symlink`);
|
|
100
|
+
}
|
|
101
|
+
if (pathStat.size !== expectedSize) {
|
|
102
|
+
throw new Error(`${label} size does not match the signed release descriptor`);
|
|
103
|
+
}
|
|
104
|
+
const noFollow = typeof fs.constants.O_NOFOLLOW === "number"
|
|
105
|
+
? fs.constants.O_NOFOLLOW
|
|
106
|
+
: 0;
|
|
107
|
+
const fd = fs.openSync(pathname, fs.constants.O_RDONLY | fs.constants.O_NONBLOCK | noFollow);
|
|
108
|
+
try {
|
|
109
|
+
const openedStat = fs.fstatSync(fd);
|
|
110
|
+
if (!openedStat.isFile() || openedStat.size !== expectedSize) {
|
|
111
|
+
throw new Error(`${label} changed before it could be read safely`);
|
|
112
|
+
}
|
|
113
|
+
const bytes = Buffer.alloc(expectedSize + 1);
|
|
114
|
+
let offset = 0;
|
|
115
|
+
while (offset < bytes.length) {
|
|
116
|
+
const count = fs.readSync(fd, bytes, offset, bytes.length - offset, null);
|
|
117
|
+
if (count === 0)
|
|
118
|
+
break;
|
|
119
|
+
offset += count;
|
|
120
|
+
}
|
|
121
|
+
if (offset !== expectedSize) {
|
|
122
|
+
throw new Error(`${label} changed size while being read`);
|
|
123
|
+
}
|
|
124
|
+
const verified = bytes.subarray(0, expectedSize);
|
|
125
|
+
const digest = createHash("sha256").update(verified).digest("hex");
|
|
126
|
+
if (digest !== expectedSha256) {
|
|
127
|
+
throw new Error(`${label} does not match the signed release descriptor`);
|
|
128
|
+
}
|
|
129
|
+
return verified;
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
fs.closeSync(fd);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export function loadFortemiCoreExport(cwd, graph = "project", sourcePaths) {
|
|
136
|
+
if (sourcePaths) {
|
|
137
|
+
try {
|
|
138
|
+
const manifestBytes = readVerifiedRegularFile(sourcePaths.manifestPath, sourcePaths.manifestSize, sourcePaths.manifestSha256, "Verified Fortemi Core manifest");
|
|
139
|
+
const exportBytes = readVerifiedRegularFile(sourcePaths.exportPath, sourcePaths.exportSize, sourcePaths.exportSha256, "Verified Fortemi Core export");
|
|
140
|
+
const manifestSha256 = createHash("sha256").update(manifestBytes).digest("hex");
|
|
141
|
+
const exportSha256 = createHash("sha256").update(exportBytes).digest("hex");
|
|
142
|
+
if (manifestSha256 !== sourcePaths.manifestSha256 ||
|
|
143
|
+
exportSha256 !== sourcePaths.exportSha256) {
|
|
144
|
+
return { reason: "Verified Fortemi Core source bytes do not match the signed release descriptor." };
|
|
145
|
+
}
|
|
146
|
+
const manifest = JSON.parse(manifestBytes.toString("utf-8"));
|
|
147
|
+
const exported = JSON.parse(exportBytes.toString("utf-8"));
|
|
148
|
+
if (manifest.schema_version !== "aiwg.fortemi.prebuilt.v1" ||
|
|
149
|
+
manifest.backend !== "fortemi-core" ||
|
|
150
|
+
manifest.graph !== graph ||
|
|
151
|
+
manifest.export_path !== "aiwg-fortemi-index-v2.json" ||
|
|
152
|
+
manifest.export_schema_version !== "aiwg.fortemi.index.export.v2") {
|
|
153
|
+
return { reason: "Verified Fortemi Core source manifest is incompatible with the requested graph." };
|
|
154
|
+
}
|
|
155
|
+
if (manifest.export_checksum !== exportSha256 ||
|
|
156
|
+
exported.schema_version !== "aiwg.fortemi.index.export.v2" ||
|
|
157
|
+
exported.source?.graph !== graph ||
|
|
158
|
+
!Array.isArray(exported.items) ||
|
|
159
|
+
manifest.item_count !== exported.items.length) {
|
|
160
|
+
return { reason: "Verified Fortemi Core source export does not match its manifest." };
|
|
161
|
+
}
|
|
162
|
+
return { exported };
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
return {
|
|
166
|
+
reason: `Verified Fortemi Core source could not be read: ${err instanceof Error ? err.message : String(err)}`,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
89
170
|
let status = getFortemiCoreSyncStatus(cwd, graph);
|
|
90
171
|
if ((!status.optedIn || !status.built || status.stale) && graph === "framework") {
|
|
91
172
|
const prebuilt = getFortemiCorePrebuiltStatus(graph);
|
|
@@ -164,16 +245,17 @@ function entryFromRecord(record) {
|
|
|
164
245
|
kernel: typeof record.search?.frontmatter?.kernel === "boolean"
|
|
165
246
|
? record.search.frontmatter.kernel
|
|
166
247
|
: undefined,
|
|
248
|
+
operationalState: record.operational_state,
|
|
167
249
|
};
|
|
168
250
|
}
|
|
169
|
-
export function loadFortemiCoreMetadataEntries(cwd, graph = "project") {
|
|
170
|
-
const loaded = loadFortemiCoreExport(cwd, graph);
|
|
251
|
+
export function loadFortemiCoreMetadataEntries(cwd, graph = "project", sourcePaths) {
|
|
252
|
+
const loaded = loadFortemiCoreExport(cwd, graph, sourcePaths);
|
|
171
253
|
if (!loaded.exported)
|
|
172
254
|
return { entries: [], reason: loaded.reason };
|
|
173
255
|
return { entries: loaded.exported.items.map(entryFromRecord) };
|
|
174
256
|
}
|
|
175
257
|
export async function queryFortemiCoreAiwgDiscovery(cwd, options) {
|
|
176
|
-
const loaded = loadFortemiCoreExport(cwd, options.graph ?? "framework");
|
|
258
|
+
const loaded = loadFortemiCoreExport(cwd, options.graph ?? "framework", options.sourcePaths);
|
|
177
259
|
if (!loaded.exported) {
|
|
178
260
|
return {
|
|
179
261
|
results: [],
|
|
@@ -328,6 +410,9 @@ export function queryFortemiCoreStaticFulltextIndex(cwd, options) {
|
|
|
328
410
|
summary: record.summary ?? record.search?.summary ?? record.text,
|
|
329
411
|
score: Math.round(hit.score * 1000) / 1000,
|
|
330
412
|
matched: hit.matchedTerms,
|
|
413
|
+
...(record.operational_state
|
|
414
|
+
? { operational_state: operationalStateQueryProjection(record.operational_state) }
|
|
415
|
+
: {}),
|
|
331
416
|
};
|
|
332
417
|
}),
|
|
333
418
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
-
import { GRAPH_CONFIGS, loadGlobalGraphConfigs } from "./types.js";
|
|
4
|
+
import { GRAPH_CONFIGS, getProjectIndexRoot, loadGlobalGraphConfigs } from "./types.js";
|
|
5
5
|
import { buildAiwgFortemiIndexExport, } from "./browser-export.js";
|
|
6
6
|
import { loadGraphIndexFile } from "./index-reader.js";
|
|
7
7
|
function syncDir(cwd, graph) {
|
|
@@ -11,7 +11,7 @@ function syncDir(cwd, graph) {
|
|
|
11
11
|
const xdgData = process.env.XDG_DATA_HOME ?? path.join(process.env.HOME ?? cwd, ".local", "share");
|
|
12
12
|
return path.join(xdgData, "aiwg", "index", "fortemi-core", graph);
|
|
13
13
|
}
|
|
14
|
-
return path.join(cwd, "
|
|
14
|
+
return path.join(getProjectIndexRoot(cwd), "fortemi-core", graph);
|
|
15
15
|
}
|
|
16
16
|
function findPackageRoot(startDir) {
|
|
17
17
|
let current = startDir;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { promises as fs } from 'fs';
|
|
11
11
|
import path from 'path';
|
|
12
|
+
import { resolveProjectAiwgDir } from '../config/project-artifacts.js';
|
|
12
13
|
// ============================================================================
|
|
13
14
|
// ArtifactIndex
|
|
14
15
|
// ============================================================================
|
|
@@ -24,7 +25,7 @@ export class ArtifactIndex {
|
|
|
24
25
|
*/
|
|
25
26
|
async build() {
|
|
26
27
|
this.artifacts = [];
|
|
27
|
-
const aiwgDir =
|
|
28
|
+
const aiwgDir = resolveProjectAiwgDir(this.projectPath);
|
|
28
29
|
try {
|
|
29
30
|
await this.scanDirectory(aiwgDir, '');
|
|
30
31
|
}
|
|
@@ -13,30 +13,50 @@ import fs from 'fs';
|
|
|
13
13
|
import path from 'path';
|
|
14
14
|
import { createHash } from 'crypto';
|
|
15
15
|
import { load as loadYaml } from 'js-yaml';
|
|
16
|
-
import { DEFAULT_INDEX_EXTENSIONS, INDEX_EXTRACTOR_VERSION, INDEX_VERSION, INDEX_DIR, OPERATIONAL_DISCOVERY_TYPES, PHASE_DIRECTORIES, GRAPH_CONFIGS, TEMPLATE_INDEX_EXTENSIONS, loadUserGraphConfigs, loadGlobalGraphConfigs, } from './types.js';
|
|
16
|
+
import { DEFAULT_INDEX_EXTENSIONS, INDEX_EXTRACTOR_VERSION, INDEX_VERSION, INDEX_DIR, OPERATIONAL_DISCOVERY_TYPES, PHASE_DIRECTORIES, GRAPH_CONFIGS, TEMPLATE_INDEX_EXTENSIONS, resolveGraphScanDir, loadUserGraphConfigs, loadGlobalGraphConfigs, } from './types.js';
|
|
17
17
|
import { parseCitationSidecar, citationResultToEdges, buildRefToPathMap } from './citation-parser.js';
|
|
18
18
|
import { writeIndexFile, resolveIndexDir, loadGraphIndexFile } from './index-reader.js';
|
|
19
19
|
import { loadManifest, writeManifest, statMatches, makeEntry } from './checksum-manifest.js';
|
|
20
20
|
import { workspaceLinkedFiles } from '../smiths/context-pipeline/workspace-context.js';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return path.join(cwd, scanDir);
|
|
21
|
+
import { normalizeOperationalState } from './operational-state.js';
|
|
22
|
+
import { DEFAULT_PROJECT_AIWG_DIR, resolveProjectAiwgDir, } from '../config/project-artifacts.js';
|
|
23
|
+
function pathContains(parent, child) {
|
|
24
|
+
const relative = path.relative(parent, child);
|
|
25
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
26
|
+
}
|
|
27
|
+
function toPosixPath(value) {
|
|
28
|
+
return value.split(path.sep).join('/');
|
|
30
29
|
}
|
|
31
|
-
function indexPathFor(cwd, fullPath) {
|
|
30
|
+
function indexPathFor(cwd, fullPath, graph) {
|
|
31
|
+
if (!graph || graph === 'project') {
|
|
32
|
+
const artifactRoot = resolveProjectAiwgDir(cwd);
|
|
33
|
+
if (pathContains(artifactRoot, fullPath)) {
|
|
34
|
+
const relative = toPosixPath(path.relative(artifactRoot, fullPath));
|
|
35
|
+
return relative ? `${DEFAULT_PROJECT_AIWG_DIR}/${relative}` : DEFAULT_PROJECT_AIWG_DIR;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
32
38
|
const rel = path.relative(cwd, fullPath);
|
|
33
39
|
if (!rel.startsWith('..') && !path.isAbsolute(rel))
|
|
34
|
-
return rel;
|
|
40
|
+
return toPosixPath(rel);
|
|
35
41
|
return fullPath;
|
|
36
42
|
}
|
|
37
|
-
function absoluteEntryPath(cwd, entryPath) {
|
|
43
|
+
function absoluteEntryPath(cwd, entryPath, graph) {
|
|
44
|
+
if ((!graph || graph === 'project') && entryPath.startsWith(`${DEFAULT_PROJECT_AIWG_DIR}/`)) {
|
|
45
|
+
return path.join(resolveProjectAiwgDir(cwd), entryPath.slice(DEFAULT_PROJECT_AIWG_DIR.length + 1));
|
|
46
|
+
}
|
|
38
47
|
return path.isAbsolute(entryPath) ? entryPath : path.join(cwd, entryPath);
|
|
39
48
|
}
|
|
49
|
+
function loadIndexFromDir(indexDir, filename) {
|
|
50
|
+
const filePath = path.join(indexDir, filename);
|
|
51
|
+
if (!fs.existsSync(filePath))
|
|
52
|
+
return null;
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
40
60
|
/**
|
|
41
61
|
* Parse YAML frontmatter from markdown content
|
|
42
62
|
*/
|
|
@@ -664,12 +684,12 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
664
684
|
fileExtensions = [...DEFAULT_INDEX_EXTENSIONS];
|
|
665
685
|
}
|
|
666
686
|
else if (graphConfig) {
|
|
667
|
-
scanDirs = graphConfig.scanDirs.map(d =>
|
|
687
|
+
scanDirs = graphConfig.scanDirs.map(d => resolveGraphScanDir(cwd, d));
|
|
668
688
|
fileExtensions = graphConfig.extensions;
|
|
669
689
|
}
|
|
670
690
|
else {
|
|
671
691
|
// Default: scan .aiwg/ (backward compatible)
|
|
672
|
-
scanDirs = [
|
|
692
|
+
scanDirs = [resolveProjectAiwgDir(cwd)];
|
|
673
693
|
fileExtensions = [...DEFAULT_INDEX_EXTENSIONS];
|
|
674
694
|
}
|
|
675
695
|
// Verify at least one scan directory exists
|
|
@@ -699,7 +719,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
699
719
|
indexOutputDir = resolveIndexDir(cwd, graph);
|
|
700
720
|
}
|
|
701
721
|
else {
|
|
702
|
-
indexOutputDir =
|
|
722
|
+
indexOutputDir = resolveIndexDir(cwd);
|
|
703
723
|
}
|
|
704
724
|
fs.mkdirSync(indexOutputDir, { recursive: true });
|
|
705
725
|
// effectiveOutputCwd is used for backward-compat loadMetadataIndex calls
|
|
@@ -718,7 +738,11 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
718
738
|
return;
|
|
719
739
|
}
|
|
720
740
|
// Load existing index for incremental updates
|
|
721
|
-
const existingIndex = force
|
|
741
|
+
const existingIndex = force
|
|
742
|
+
? null
|
|
743
|
+
: outputDir
|
|
744
|
+
? loadIndexFromDir(indexOutputDir, 'metadata.json')
|
|
745
|
+
: loadGraphIndexFile(effectiveOutputCwd, 'metadata.json', graph);
|
|
722
746
|
const canReuseExisting = existingIndex?.version === INDEX_VERSION
|
|
723
747
|
&& existingIndex.extractorVersion === INDEX_EXTRACTOR_VERSION;
|
|
724
748
|
const existingEntries = canReuseExisting ? existingIndex.entries : {};
|
|
@@ -762,7 +786,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
762
786
|
let unchangedCount = 0;
|
|
763
787
|
const useFilenameMetadata = graphConfig?.nodeStrategy === 'filename-metadata';
|
|
764
788
|
for (const fullPath of files) {
|
|
765
|
-
const relativePath = indexPathFor(cwd, fullPath);
|
|
789
|
+
const relativePath = indexPathFor(cwd, fullPath, graph);
|
|
766
790
|
let entry;
|
|
767
791
|
if (useFilenameMetadata) {
|
|
768
792
|
// Filename-metadata strategy: derive metadata from filename, skip content read.
|
|
@@ -857,6 +881,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
857
881
|
const kernel = data.kernel === true || data.kernel === 'true' ? true : undefined;
|
|
858
882
|
// Script entrypoint metadata is meaningful for skills only (#1227).
|
|
859
883
|
const script = type === 'skill' ? extractSkillScript(data) : undefined;
|
|
884
|
+
const operationalState = normalizeOperationalState(data.operational_state);
|
|
860
885
|
// Canonical short name (#1233) — used by the scorer to floor exact-name
|
|
861
886
|
// queries to 1.0 so hyphenated kernel-skill names like `aiwg-doctor`
|
|
862
887
|
// remain searchable even when the rendered title strips the hyphen.
|
|
@@ -881,6 +906,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
881
906
|
...(searchTerms && searchTerms.length > 0 ? { searchTerms } : {}),
|
|
882
907
|
...(kernel ? { kernel } : {}),
|
|
883
908
|
...(script ? { script } : {}),
|
|
909
|
+
...(operationalState ? { operationalState } : {}),
|
|
884
910
|
};
|
|
885
911
|
}
|
|
886
912
|
entries[relativePath] = entry;
|
|
@@ -935,7 +961,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
935
961
|
// Build REF-XXX → path map from all entries with ref frontmatter
|
|
936
962
|
const entryFrontmatter = new Map();
|
|
937
963
|
for (const entryPath of Object.keys(entries)) {
|
|
938
|
-
const fullPath = absoluteEntryPath(cwd, entryPath);
|
|
964
|
+
const fullPath = absoluteEntryPath(cwd, entryPath, graph);
|
|
939
965
|
if (fs.existsSync(fullPath)) {
|
|
940
966
|
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
941
967
|
const { data } = parseFrontmatter(content);
|
|
@@ -946,7 +972,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
946
972
|
// Parse each entry as a citation sidecar and extract edges
|
|
947
973
|
let citationEdgeCount = 0;
|
|
948
974
|
for (const entryPath of Object.keys(entries)) {
|
|
949
|
-
const fullPath = absoluteEntryPath(cwd, entryPath);
|
|
975
|
+
const fullPath = absoluteEntryPath(cwd, entryPath, graph);
|
|
950
976
|
if (!fs.existsSync(fullPath))
|
|
951
977
|
continue;
|
|
952
978
|
const content = fs.readFileSync(fullPath, 'utf-8');
|