@ornexus/neocortex 4.0.1
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.
Potentially problematic release.
This version of @ornexus/neocortex might be problematic. Click here for more details.
- package/LICENSE +56 -0
- package/README.md +32 -0
- package/install.js +486 -0
- package/install.ps1 +1790 -0
- package/install.sh +1587 -0
- package/package.json +104 -0
- package/packages/client/dist/adapters/adapter-registry.d.ts +61 -0
- package/packages/client/dist/adapters/adapter-registry.js +106 -0
- package/packages/client/dist/adapters/antigravity-adapter.d.ts +18 -0
- package/packages/client/dist/adapters/antigravity-adapter.js +77 -0
- package/packages/client/dist/adapters/claude-code-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/claude-code-adapter.js +79 -0
- package/packages/client/dist/adapters/codex-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/codex-adapter.js +80 -0
- package/packages/client/dist/adapters/cursor-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/cursor-adapter.js +115 -0
- package/packages/client/dist/adapters/gemini-adapter.d.ts +18 -0
- package/packages/client/dist/adapters/gemini-adapter.js +71 -0
- package/packages/client/dist/adapters/index.d.ts +19 -0
- package/packages/client/dist/adapters/index.js +21 -0
- package/packages/client/dist/adapters/platform-detector.d.ts +46 -0
- package/packages/client/dist/adapters/platform-detector.js +106 -0
- package/packages/client/dist/adapters/target-adapter.d.ts +70 -0
- package/packages/client/dist/adapters/target-adapter.js +12 -0
- package/packages/client/dist/adapters/vscode-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/vscode-adapter.js +72 -0
- package/packages/client/dist/agent/refresh-stubs.d.ts +65 -0
- package/packages/client/dist/agent/refresh-stubs.js +234 -0
- package/packages/client/dist/agent/update-agent-yaml.d.ts +26 -0
- package/packages/client/dist/agent/update-agent-yaml.js +102 -0
- package/packages/client/dist/agent/update-description.d.ts +45 -0
- package/packages/client/dist/agent/update-description.js +251 -0
- package/packages/client/dist/cache/crypto-utils.d.ts +30 -0
- package/packages/client/dist/cache/crypto-utils.js +76 -0
- package/packages/client/dist/cache/encrypted-cache.d.ts +30 -0
- package/packages/client/dist/cache/encrypted-cache.js +94 -0
- package/packages/client/dist/cache/in-memory-asset-cache.d.ts +59 -0
- package/packages/client/dist/cache/in-memory-asset-cache.js +70 -0
- package/packages/client/dist/cache/index.d.ts +13 -0
- package/packages/client/dist/cache/index.js +13 -0
- package/packages/client/dist/cli.d.ts +14 -0
- package/packages/client/dist/cli.js +194 -0
- package/packages/client/dist/commands/activate.d.ts +55 -0
- package/packages/client/dist/commands/activate.js +390 -0
- package/packages/client/dist/commands/cache-status.d.ts +39 -0
- package/packages/client/dist/commands/cache-status.js +112 -0
- package/packages/client/dist/commands/invoke.d.ts +70 -0
- package/packages/client/dist/commands/invoke.js +490 -0
- package/packages/client/dist/config/resolver-selection.d.ts +40 -0
- package/packages/client/dist/config/resolver-selection.js +278 -0
- package/packages/client/dist/config/secure-config.d.ts +78 -0
- package/packages/client/dist/config/secure-config.js +269 -0
- package/packages/client/dist/constants.d.ts +25 -0
- package/packages/client/dist/constants.js +25 -0
- package/packages/client/dist/context/context-collector.d.ts +28 -0
- package/packages/client/dist/context/context-collector.js +222 -0
- package/packages/client/dist/context/context-sanitizer.d.ts +28 -0
- package/packages/client/dist/context/context-sanitizer.js +145 -0
- package/packages/client/dist/index.d.ts +55 -0
- package/packages/client/dist/index.js +38 -0
- package/packages/client/dist/license/index.d.ts +5 -0
- package/packages/client/dist/license/index.js +5 -0
- package/packages/client/dist/license/license-client.d.ts +79 -0
- package/packages/client/dist/license/license-client.js +257 -0
- package/packages/client/dist/machine/fingerprint.d.ts +34 -0
- package/packages/client/dist/machine/fingerprint.js +160 -0
- package/packages/client/dist/machine/index.d.ts +5 -0
- package/packages/client/dist/machine/index.js +5 -0
- package/packages/client/dist/resilience/circuit-breaker.d.ts +70 -0
- package/packages/client/dist/resilience/circuit-breaker.js +170 -0
- package/packages/client/dist/resilience/degradation-manager.d.ts +67 -0
- package/packages/client/dist/resilience/degradation-manager.js +164 -0
- package/packages/client/dist/resilience/freshness-indicator.d.ts +59 -0
- package/packages/client/dist/resilience/freshness-indicator.js +100 -0
- package/packages/client/dist/resilience/index.d.ts +8 -0
- package/packages/client/dist/resilience/index.js +8 -0
- package/packages/client/dist/resilience/recovery-detector.d.ts +59 -0
- package/packages/client/dist/resilience/recovery-detector.js +74 -0
- package/packages/client/dist/resolvers/asset-resolver.d.ts +79 -0
- package/packages/client/dist/resolvers/asset-resolver.js +13 -0
- package/packages/client/dist/resolvers/local-resolver.d.ts +26 -0
- package/packages/client/dist/resolvers/local-resolver.js +218 -0
- package/packages/client/dist/resolvers/remote-resolver.d.ts +91 -0
- package/packages/client/dist/resolvers/remote-resolver.js +282 -0
- package/packages/client/dist/telemetry/index.d.ts +5 -0
- package/packages/client/dist/telemetry/index.js +5 -0
- package/packages/client/dist/telemetry/offline-queue.d.ts +57 -0
- package/packages/client/dist/telemetry/offline-queue.js +131 -0
- package/packages/client/dist/tier/index.d.ts +5 -0
- package/packages/client/dist/tier/index.js +5 -0
- package/packages/client/dist/tier/tier-aware-client.d.ts +97 -0
- package/packages/client/dist/tier/tier-aware-client.js +260 -0
- package/packages/client/dist/types/index.d.ts +140 -0
- package/packages/client/dist/types/index.js +38 -0
- package/postinstall.js +272 -0
- package/targets-stubs/antigravity/README.md +36 -0
- package/targets-stubs/antigravity/gemini.md +22 -0
- package/targets-stubs/antigravity/install-antigravity.sh +44 -0
- package/targets-stubs/antigravity/mcp-config.json +9 -0
- package/targets-stubs/antigravity/skill/SKILL.md +67 -0
- package/targets-stubs/claude-code/README.md +20 -0
- package/targets-stubs/claude-code/neocortex.agent.yaml +24 -0
- package/targets-stubs/claude-code/neocortex.md +125 -0
- package/targets-stubs/codex/README.md +32 -0
- package/targets-stubs/codex/agents.md +61 -0
- package/targets-stubs/codex/config-mcp.toml +6 -0
- package/targets-stubs/codex/install-codex.sh +61 -0
- package/targets-stubs/cursor/README.md +33 -0
- package/targets-stubs/cursor/agent.md +94 -0
- package/targets-stubs/cursor/install-cursor.sh +35 -0
- package/targets-stubs/cursor/mcp.json +11 -0
- package/targets-stubs/gemini-cli/README.md +34 -0
- package/targets-stubs/gemini-cli/agent.md +101 -0
- package/targets-stubs/gemini-cli/gemini.md +16 -0
- package/targets-stubs/gemini-cli/install-gemini.sh +56 -0
- package/targets-stubs/gemini-cli/settings-mcp.json +11 -0
- package/targets-stubs/vscode/README.md +34 -0
- package/targets-stubs/vscode/agent.md +102 -0
- package/targets-stubs/vscode/copilot-instructions.md +16 -0
- package/targets-stubs/vscode/install-vscode.sh +42 -0
- package/targets-stubs/vscode/mcp.json +13 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license FSL-1.1
|
|
3
|
+
* Copyright (c) 2026 OrNexus AI
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Neocortex CLI, licensed under the
|
|
6
|
+
* Functional Source License, Version 1.1 (FSL-1.1).
|
|
7
|
+
*
|
|
8
|
+
* Change Date: February 20, 2029
|
|
9
|
+
* Change License: MIT
|
|
10
|
+
*
|
|
11
|
+
* See the LICENSE file in the project root for full license text.
|
|
12
|
+
*/
|
|
13
|
+
// ── RecoveryDetector ─────────────────────────────────────────────────────
|
|
14
|
+
export class RecoveryDetector {
|
|
15
|
+
actions;
|
|
16
|
+
constructor(actions) {
|
|
17
|
+
this.actions = actions;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Execute full recovery sequence.
|
|
21
|
+
* Each step is independent - failures are captured but don't block others.
|
|
22
|
+
*/
|
|
23
|
+
async onRecovery() {
|
|
24
|
+
const result = {
|
|
25
|
+
circuitClosed: false,
|
|
26
|
+
jwtRefreshed: false,
|
|
27
|
+
cacheSynced: false,
|
|
28
|
+
telemetryFlushed: { sent: 0, failed: 0 },
|
|
29
|
+
notification: '[ONLINE] Server connection restored',
|
|
30
|
+
};
|
|
31
|
+
// Step 1: Close circuit breaker
|
|
32
|
+
try {
|
|
33
|
+
await this.actions.circuitBreaker.recordSuccess();
|
|
34
|
+
result.circuitClosed = true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Non-critical: breaker will close on next successful call
|
|
38
|
+
}
|
|
39
|
+
// Step 2: Re-validate JWT
|
|
40
|
+
try {
|
|
41
|
+
const token = await this.actions.licenseClient.getToken();
|
|
42
|
+
result.jwtRefreshed = token !== null;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Non-critical: will be retried on next server call
|
|
46
|
+
}
|
|
47
|
+
// Step 3: Sync cache (background - just attempt, don't block)
|
|
48
|
+
try {
|
|
49
|
+
// We don't actually sync here - the next resolver call will
|
|
50
|
+
// update the cache automatically via fetchWithCacheFallback.
|
|
51
|
+
// We just mark as synced since the circuit is now closed.
|
|
52
|
+
result.cacheSynced = true;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Non-critical
|
|
56
|
+
}
|
|
57
|
+
// Step 4: Flush telemetry queue
|
|
58
|
+
try {
|
|
59
|
+
const flushResult = await this.actions.telemetryQueue.flush(async (events) => {
|
|
60
|
+
const response = await fetch(`${this.actions.serverUrl}/api/v1/telemetry/batch`, {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: { 'Content-Type': 'application/json' },
|
|
63
|
+
body: JSON.stringify({ events }),
|
|
64
|
+
});
|
|
65
|
+
return response.ok;
|
|
66
|
+
});
|
|
67
|
+
result.telemetryFlushed = flushResult;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Non-critical: queue will be flushed on next recovery
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license FSL-1.1
|
|
3
|
+
* Copyright (c) 2026 OrNexus AI
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Neocortex CLI, licensed under the
|
|
6
|
+
* Functional Source License, Version 1.1 (FSL-1.1).
|
|
7
|
+
*
|
|
8
|
+
* Change Date: February 20, 2029
|
|
9
|
+
* Change License: MIT
|
|
10
|
+
*
|
|
11
|
+
* See the LICENSE file in the project root for full license text.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @neocortex/client - AssetResolver Interface
|
|
15
|
+
*
|
|
16
|
+
* Strategy pattern interface for resolving pipeline assets.
|
|
17
|
+
* Implementations: LocalResolver (filesystem) and RemoteResolver (HTTP API).
|
|
18
|
+
*/
|
|
19
|
+
import type { AssembledPrompt, PipelineContext, ResolverMode, SkillContent, StandardContent, StepContent, StepRegistry } from '../types/index.js';
|
|
20
|
+
/**
|
|
21
|
+
* AssetResolver - Core interface for the thin client abstraction layer.
|
|
22
|
+
*
|
|
23
|
+
* Enables the CLI to resolve pipeline assets (steps, skills, standards)
|
|
24
|
+
* from either local filesystem or remote server, transparently.
|
|
25
|
+
*/
|
|
26
|
+
export interface AssetResolver {
|
|
27
|
+
/** The mode this resolver operates in */
|
|
28
|
+
readonly mode: ResolverMode;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve a pipeline step by its ID.
|
|
31
|
+
*
|
|
32
|
+
* @param stepId - Step identifier (e.g., "step-c-01-setup-branch")
|
|
33
|
+
* @param context - Current pipeline context for variable substitution
|
|
34
|
+
* @returns Resolved step content with parsed frontmatter
|
|
35
|
+
*/
|
|
36
|
+
resolveStep(stepId: string, context: PipelineContext): Promise<StepContent>;
|
|
37
|
+
/**
|
|
38
|
+
* Resolve a skill by its ID.
|
|
39
|
+
*
|
|
40
|
+
* @param skillId - Skill identifier (e.g., "tdd-guardian")
|
|
41
|
+
* @param context - Current pipeline context
|
|
42
|
+
* @returns Resolved skill content with metadata
|
|
43
|
+
*/
|
|
44
|
+
resolveSkill(skillId: string, context: PipelineContext): Promise<SkillContent>;
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a standard by its ID.
|
|
47
|
+
*
|
|
48
|
+
* @param standardId - Standard identifier (e.g., "testing/tdd-practices")
|
|
49
|
+
* @returns Resolved standard content
|
|
50
|
+
*/
|
|
51
|
+
resolveStandard(standardId: string): Promise<StandardContent>;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the step registry (step-registry.json).
|
|
54
|
+
*
|
|
55
|
+
* @returns Parsed step registry with all step definitions
|
|
56
|
+
*/
|
|
57
|
+
resolveRegistry(): Promise<StepRegistry>;
|
|
58
|
+
/**
|
|
59
|
+
* Assemble a complete prompt for a step.
|
|
60
|
+
*
|
|
61
|
+
* Combines step content, required skills, and standards with
|
|
62
|
+
* variable substitution from the pipeline context.
|
|
63
|
+
*
|
|
64
|
+
* @param stepId - Step to assemble prompt for
|
|
65
|
+
* @param context - Current pipeline context
|
|
66
|
+
* @returns Fully assembled prompt ready for execution
|
|
67
|
+
*/
|
|
68
|
+
assemblePrompt(stepId: string, context: PipelineContext): Promise<AssembledPrompt>;
|
|
69
|
+
/**
|
|
70
|
+
* Check if this resolver is available and properly configured.
|
|
71
|
+
*
|
|
72
|
+
* @returns true if resolver can serve requests
|
|
73
|
+
*/
|
|
74
|
+
isAvailable(): Promise<boolean>;
|
|
75
|
+
/**
|
|
76
|
+
* Release any resources held by this resolver.
|
|
77
|
+
*/
|
|
78
|
+
dispose(): Promise<void>;
|
|
79
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license FSL-1.1
|
|
3
|
+
* Copyright (c) 2026 OrNexus AI
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Neocortex CLI, licensed under the
|
|
6
|
+
* Functional Source License, Version 1.1 (FSL-1.1).
|
|
7
|
+
*
|
|
8
|
+
* Change Date: February 20, 2029
|
|
9
|
+
* Change License: MIT
|
|
10
|
+
*
|
|
11
|
+
* See the LICENSE file in the project root for full license text.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license FSL-1.1
|
|
3
|
+
* Copyright (c) 2026 OrNexus AI
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Neocortex CLI, licensed under the
|
|
6
|
+
* Functional Source License, Version 1.1 (FSL-1.1).
|
|
7
|
+
*
|
|
8
|
+
* Change Date: February 20, 2029
|
|
9
|
+
* Change License: MIT
|
|
10
|
+
*
|
|
11
|
+
* See the LICENSE file in the project root for full license text.
|
|
12
|
+
*/
|
|
13
|
+
import type { AssetResolver } from './asset-resolver.js';
|
|
14
|
+
import { ResolverMode, type AssembledPrompt, type LocalResolverOptions, type PipelineContext, type SkillContent, type StandardContent, type StepContent, type StepRegistry } from '../types/index.js';
|
|
15
|
+
export declare class LocalResolver implements AssetResolver {
|
|
16
|
+
readonly mode = ResolverMode.LOCAL;
|
|
17
|
+
private readonly projectRoot;
|
|
18
|
+
constructor(options: LocalResolverOptions);
|
|
19
|
+
resolveStep(stepId: string, _context: PipelineContext): Promise<StepContent>;
|
|
20
|
+
resolveSkill(skillId: string, _context: PipelineContext): Promise<SkillContent>;
|
|
21
|
+
resolveStandard(standardId: string): Promise<StandardContent>;
|
|
22
|
+
resolveRegistry(): Promise<StepRegistry>;
|
|
23
|
+
assemblePrompt(stepId: string, context: PipelineContext): Promise<AssembledPrompt>;
|
|
24
|
+
isAvailable(): Promise<boolean>;
|
|
25
|
+
dispose(): Promise<void>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license FSL-1.1
|
|
3
|
+
* Copyright (c) 2026 OrNexus AI
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Neocortex CLI, licensed under the
|
|
6
|
+
* Functional Source License, Version 1.1 (FSL-1.1).
|
|
7
|
+
*
|
|
8
|
+
* Change Date: February 20, 2029
|
|
9
|
+
* Change License: MIT
|
|
10
|
+
*
|
|
11
|
+
* See the LICENSE file in the project root for full license text.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @neocortex/client - LocalResolver
|
|
15
|
+
*
|
|
16
|
+
* Resolves pipeline assets from the local filesystem.
|
|
17
|
+
* Used in development mode when core/ directory is available locally.
|
|
18
|
+
* Replicates the current CLI behavior of reading .md files directly.
|
|
19
|
+
*/
|
|
20
|
+
import { readFile, access } from 'node:fs/promises';
|
|
21
|
+
import { join, resolve } from 'node:path';
|
|
22
|
+
import { ResolverMode, } from '../types/index.js';
|
|
23
|
+
// ── Frontmatter Parser ───────────────────────────────────────────────────
|
|
24
|
+
const FRONTMATTER_REGEX = /^---\n([\s\S]*?)\n---/;
|
|
25
|
+
/**
|
|
26
|
+
* Parse YAML-like frontmatter from markdown content.
|
|
27
|
+
* Simple key-value parser sufficient for step/skill frontmatter.
|
|
28
|
+
*/
|
|
29
|
+
function parseFrontmatter(content) {
|
|
30
|
+
const match = content.match(FRONTMATTER_REGEX);
|
|
31
|
+
if (!match) {
|
|
32
|
+
return { frontmatter: {}, body: content };
|
|
33
|
+
}
|
|
34
|
+
const frontmatter = {};
|
|
35
|
+
const lines = match[1].split('\n');
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
const colonIndex = line.indexOf(':');
|
|
38
|
+
if (colonIndex === -1)
|
|
39
|
+
continue;
|
|
40
|
+
const key = line.slice(0, colonIndex).trim().replace(/^['"]|['"]$/g, '');
|
|
41
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
42
|
+
// Remove surrounding quotes
|
|
43
|
+
if (typeof value === 'string') {
|
|
44
|
+
value = value.replace(/^['"]|['"]$/g, '');
|
|
45
|
+
}
|
|
46
|
+
// Parse booleans
|
|
47
|
+
if (value === 'true')
|
|
48
|
+
value = true;
|
|
49
|
+
else if (value === 'false')
|
|
50
|
+
value = false;
|
|
51
|
+
frontmatter[key] = value;
|
|
52
|
+
}
|
|
53
|
+
const body = content.slice(match[0].length).trimStart();
|
|
54
|
+
return { frontmatter, body };
|
|
55
|
+
}
|
|
56
|
+
// ── Step ID to File Path Mapping ─────────────────────────────────────────
|
|
57
|
+
/**
|
|
58
|
+
* Map step ID to file path within core/steps/.
|
|
59
|
+
*
|
|
60
|
+
* Step IDs follow the pattern: step-{category}-{order}-{name}
|
|
61
|
+
* File paths: core/steps/steps-{category}/{id}.md
|
|
62
|
+
*/
|
|
63
|
+
function stepIdToPath(stepId) {
|
|
64
|
+
// Extract category from step ID: step-c-01-setup-branch -> c
|
|
65
|
+
// step-r-fix-blocked -> r, step-u-init -> u, step-p-01-idea-diagnose -> p, step-e-01-epic-init -> e
|
|
66
|
+
const parts = stepId.split('-');
|
|
67
|
+
if (parts.length < 3) {
|
|
68
|
+
throw new Error(`Invalid step ID format: ${stepId}`);
|
|
69
|
+
}
|
|
70
|
+
const category = parts[1]; // c, r, u, p, e
|
|
71
|
+
const categoryMap = {
|
|
72
|
+
c: 'steps-c',
|
|
73
|
+
r: 'steps-r',
|
|
74
|
+
u: 'steps-u',
|
|
75
|
+
p: 'steps-p',
|
|
76
|
+
e: 'steps-e',
|
|
77
|
+
};
|
|
78
|
+
const dir = categoryMap[category];
|
|
79
|
+
if (!dir) {
|
|
80
|
+
throw new Error(`Unknown step category '${category}' in step ID: ${stepId}`);
|
|
81
|
+
}
|
|
82
|
+
return `core/steps/${dir}/${stepId}.md`;
|
|
83
|
+
}
|
|
84
|
+
// ── LocalResolver Implementation ─────────────────────────────────────────
|
|
85
|
+
export class LocalResolver {
|
|
86
|
+
mode = ResolverMode.LOCAL;
|
|
87
|
+
projectRoot;
|
|
88
|
+
constructor(options) {
|
|
89
|
+
this.projectRoot = resolve(options.projectRoot);
|
|
90
|
+
}
|
|
91
|
+
async resolveStep(stepId, _context) {
|
|
92
|
+
const relativePath = stepIdToPath(stepId);
|
|
93
|
+
const fullPath = join(this.projectRoot, relativePath);
|
|
94
|
+
const raw = await readFile(fullPath, 'utf-8');
|
|
95
|
+
const { frontmatter, body } = parseFrontmatter(raw);
|
|
96
|
+
return {
|
|
97
|
+
id: stepId,
|
|
98
|
+
content: body,
|
|
99
|
+
frontmatter,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async resolveSkill(skillId, _context) {
|
|
103
|
+
// Skills are organized in: core/skills/{category}/{skillId}.md
|
|
104
|
+
// or: core/skills/step-skills/{step}/{skillId}.md
|
|
105
|
+
// Try multiple paths to find the skill
|
|
106
|
+
const searchPaths = [
|
|
107
|
+
`core/skills/${skillId}.md`,
|
|
108
|
+
`core/skills/step-skills/${skillId}.md`,
|
|
109
|
+
];
|
|
110
|
+
// Also try with nested path: "diagnose/prompt-enricher" -> core/skills/step-skills/diagnose/prompt-enricher.md
|
|
111
|
+
if (skillId.includes('/')) {
|
|
112
|
+
searchPaths.unshift(`core/skills/step-skills/${skillId}.md`);
|
|
113
|
+
searchPaths.unshift(`core/skills/${skillId}.md`);
|
|
114
|
+
}
|
|
115
|
+
let raw = null;
|
|
116
|
+
let resolvedPath = '';
|
|
117
|
+
for (const searchPath of searchPaths) {
|
|
118
|
+
const fullPath = join(this.projectRoot, searchPath);
|
|
119
|
+
try {
|
|
120
|
+
raw = await readFile(fullPath, 'utf-8');
|
|
121
|
+
resolvedPath = searchPath;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Try next path
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (raw === null) {
|
|
129
|
+
throw new Error(`Skill '${skillId}' not found. Searched: ${searchPaths.join(', ')}`);
|
|
130
|
+
}
|
|
131
|
+
const { frontmatter, body } = parseFrontmatter(raw);
|
|
132
|
+
return {
|
|
133
|
+
id: skillId,
|
|
134
|
+
content: body,
|
|
135
|
+
metadata: { ...frontmatter, resolvedPath },
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
async resolveStandard(standardId) {
|
|
139
|
+
const fullPath = join(this.projectRoot, 'core', 'standards', standardId);
|
|
140
|
+
let content;
|
|
141
|
+
try {
|
|
142
|
+
content = await readFile(fullPath, 'utf-8');
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Try with .md extension if not present
|
|
146
|
+
if (!standardId.endsWith('.md')) {
|
|
147
|
+
content = await readFile(`${fullPath}.md`, 'utf-8');
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
throw new Error(`Standard '${standardId}' not found at ${fullPath}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
id: standardId,
|
|
155
|
+
content,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
async resolveRegistry() {
|
|
159
|
+
const fullPath = join(this.projectRoot, 'core', 'data', 'step-registry.json');
|
|
160
|
+
const raw = await readFile(fullPath, 'utf-8');
|
|
161
|
+
return JSON.parse(raw);
|
|
162
|
+
}
|
|
163
|
+
async assemblePrompt(stepId, context) {
|
|
164
|
+
// Resolve the step content
|
|
165
|
+
const step = await this.resolveStep(stepId, context);
|
|
166
|
+
// Build variables map from context
|
|
167
|
+
const variables = {
|
|
168
|
+
'{story_id}': context.storyId,
|
|
169
|
+
'{story_title}': context.storyTitle,
|
|
170
|
+
'{step_id}': stepId,
|
|
171
|
+
'{step_name}': step.frontmatter['name'] || stepId,
|
|
172
|
+
'{current_status}': context.currentStatus,
|
|
173
|
+
'{epic_id}': context.epicId,
|
|
174
|
+
'{branch_name}': context.branchName,
|
|
175
|
+
'{platform_target}': context.platformTarget,
|
|
176
|
+
};
|
|
177
|
+
// Substitute variables in step content
|
|
178
|
+
let prompt = step.content;
|
|
179
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
180
|
+
prompt = prompt.replaceAll(key, value);
|
|
181
|
+
}
|
|
182
|
+
// Resolve skills referenced in step frontmatter
|
|
183
|
+
const includedSkills = [];
|
|
184
|
+
const skillsRef = step.frontmatter['skills'];
|
|
185
|
+
if (skillsRef && typeof skillsRef === 'string') {
|
|
186
|
+
const skillIds = skillsRef.split(',').map((s) => s.trim());
|
|
187
|
+
for (const sid of skillIds) {
|
|
188
|
+
try {
|
|
189
|
+
const skill = await this.resolveSkill(sid, context);
|
|
190
|
+
prompt += `\n\n---\n\n# Skill: ${sid}\n\n${skill.content}`;
|
|
191
|
+
includedSkills.push(sid);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// Skill not found - skip
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
prompt,
|
|
200
|
+
variables,
|
|
201
|
+
includedSkills,
|
|
202
|
+
includedStandards: [],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
async isAvailable() {
|
|
206
|
+
try {
|
|
207
|
+
const corePath = join(this.projectRoot, 'core');
|
|
208
|
+
await access(corePath);
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async dispose() {
|
|
216
|
+
// LocalResolver holds no resources to release
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license FSL-1.1
|
|
3
|
+
* Copyright (c) 2026 OrNexus AI
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Neocortex CLI, licensed under the
|
|
6
|
+
* Functional Source License, Version 1.1 (FSL-1.1).
|
|
7
|
+
*
|
|
8
|
+
* Change Date: February 20, 2029
|
|
9
|
+
* Change License: MIT
|
|
10
|
+
*
|
|
11
|
+
* See the LICENSE file in the project root for full license text.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @neocortex/client - RemoteResolver
|
|
15
|
+
*
|
|
16
|
+
* Resolves pipeline assets from the IP Protection Server via HTTP API.
|
|
17
|
+
* Used in production mode when core/ directory is not available locally.
|
|
18
|
+
* Supports retry with exponential backoff, timeouts, and cache fallback.
|
|
19
|
+
*/
|
|
20
|
+
import type { AssetResolver } from './asset-resolver.js';
|
|
21
|
+
import { ResolverMode, type AssembledPrompt, type PipelineContext, type RemoteResolverOptions, type SkillContent, type StandardContent, type StepContent, type StepRegistry } from '../types/index.js';
|
|
22
|
+
export declare class RemoteResolverError extends Error {
|
|
23
|
+
readonly statusCode?: number | undefined;
|
|
24
|
+
readonly endpoint?: string | undefined;
|
|
25
|
+
constructor(message: string, statusCode?: number | undefined, endpoint?: string | undefined);
|
|
26
|
+
}
|
|
27
|
+
export declare class RemoteResolver implements AssetResolver {
|
|
28
|
+
readonly mode = ResolverMode.REMOTE;
|
|
29
|
+
private readonly serverUrl;
|
|
30
|
+
private readonly licenseKey;
|
|
31
|
+
private readonly timeout;
|
|
32
|
+
private readonly retryCount;
|
|
33
|
+
/**
|
|
34
|
+
* Persistent cache -- used for registry only.
|
|
35
|
+
* P70.06: NEVER used for step/skill/standard content (those live in
|
|
36
|
+
* `assetCache` and only in process memory).
|
|
37
|
+
*/
|
|
38
|
+
private readonly cache;
|
|
39
|
+
/**
|
|
40
|
+
* In-memory LRU cache for asset content (Epic P70.06).
|
|
41
|
+
* Volatile: discarded on process exit. Never persisted to disk.
|
|
42
|
+
*/
|
|
43
|
+
private readonly assetCache;
|
|
44
|
+
private readonly licenseClient;
|
|
45
|
+
constructor(options: RemoteResolverOptions);
|
|
46
|
+
resolveStep(stepId: string, _context: PipelineContext): Promise<StepContent>;
|
|
47
|
+
resolveSkill(skillId: string, _context: PipelineContext): Promise<SkillContent>;
|
|
48
|
+
resolveStandard(standardId: string): Promise<StandardContent>;
|
|
49
|
+
resolveRegistry(): Promise<StepRegistry>;
|
|
50
|
+
assemblePrompt(stepId: string, context: PipelineContext): Promise<AssembledPrompt>;
|
|
51
|
+
isAvailable(): Promise<boolean>;
|
|
52
|
+
dispose(): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Build authorization headers for API requests.
|
|
55
|
+
* When a LicenseClient is available, uses JWT token from it.
|
|
56
|
+
* Story 31.04: NEVER sends raw license key as Bearer token.
|
|
57
|
+
* If JWT is unavailable, omits Authorization header entirely.
|
|
58
|
+
* Server will return 401, which fetchWithRetry handles via forceRefresh.
|
|
59
|
+
*/
|
|
60
|
+
private buildHeaders;
|
|
61
|
+
/**
|
|
62
|
+
* Execute HTTP request with retry and exponential backoff.
|
|
63
|
+
* Handles 401 specially: if a licenseClient is available, attempts
|
|
64
|
+
* forceRefresh() and retries once with the new token.
|
|
65
|
+
*/
|
|
66
|
+
private fetchWithRetry;
|
|
67
|
+
/**
|
|
68
|
+
* Fetch with persistent cache fallback: try HTTP first, fall back to cache
|
|
69
|
+
* on failure. On successful HTTP response, update the cache.
|
|
70
|
+
*
|
|
71
|
+
* P70.06: ONLY the registry uses this path. Asset content (step/skill/
|
|
72
|
+
* standard) uses {@link fetchWithInMemoryCache} so it never touches disk.
|
|
73
|
+
*/
|
|
74
|
+
private fetchWithCacheFallback;
|
|
75
|
+
/**
|
|
76
|
+
* P70.06: fetch with VOLATILE in-memory cache fallback.
|
|
77
|
+
*
|
|
78
|
+
* Identical structure to {@link fetchWithCacheFallback} but the backing
|
|
79
|
+
* store is {@link InMemoryAssetCache} -- entries live only in the running
|
|
80
|
+
* process memory, never written to disk.
|
|
81
|
+
*
|
|
82
|
+
* When the server is unreachable and the in-memory cache is empty (e.g.
|
|
83
|
+
* first invocation of a fresh CLI process), the original error is
|
|
84
|
+
* re-thrown instead of silently degrading to a stale disk cache.
|
|
85
|
+
*/
|
|
86
|
+
private fetchWithInMemoryCache;
|
|
87
|
+
/**
|
|
88
|
+
* Sleep for the specified duration.
|
|
89
|
+
*/
|
|
90
|
+
private sleep;
|
|
91
|
+
}
|