sinapse-ai 1.6.1 → 1.8.0
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/.claude/CLAUDE.md +5 -11
- package/.claude/hooks/README.md +14 -1
- package/.claude/hooks/code-intel-pretool.cjs +115 -0
- package/.claude/hooks/enforce-delegation.cjs +31 -3
- package/.claude/hooks/enforce-framework-boundary.cjs +324 -0
- package/.claude/hooks/enforce-permission-mode.cjs +249 -0
- package/.claude/hooks/secret-scanning.cjs +34 -43
- package/.claude/hooks/synapse-engine.cjs +23 -23
- package/.claude/hooks/telemetry-post-tool.cjs +128 -0
- package/.claude/hooks/telemetry-stop.cjs +132 -0
- package/.claude/hooks/verify-packages.cjs +9 -2
- package/.claude/rules/documentation-first.md +1 -1
- package/.claude/rules/hook-governance.md +2 -0
- package/.sinapse-ai/cli/commands/health/index.js +24 -0
- package/.sinapse-ai/core/README.md +11 -0
- package/.sinapse-ai/core/config/config-loader.js +19 -0
- package/.sinapse-ai/core/config/merge-utils.js +8 -0
- package/.sinapse-ai/core/errors/constants.js +147 -0
- package/.sinapse-ai/core/errors/error-registry.js +176 -0
- package/.sinapse-ai/core/errors/index.js +50 -0
- package/.sinapse-ai/core/errors/serializer.js +147 -0
- package/.sinapse-ai/core/errors/sinapse-error.js +144 -0
- package/.sinapse-ai/core/errors/utils.js +187 -0
- package/.sinapse-ai/core/execution/build-orchestrator.js +47 -49
- package/.sinapse-ai/core/execution/build-state-manager.js +183 -31
- package/.sinapse-ai/core/execution/parallel-executor.js +7 -1
- package/.sinapse-ai/core/execution/semantic-merge-engine.js +26 -14
- package/.sinapse-ai/core/execution/subagent-dispatcher.js +201 -60
- package/.sinapse-ai/core/execution/wave-executor.js +4 -1
- package/.sinapse-ai/core/grounding/README.md +71 -11
- package/.sinapse-ai/core/health-check/checks/project/framework-config.js +38 -2
- package/.sinapse-ai/core/health-check/checks/project/package-json.js +47 -3
- package/.sinapse-ai/core/health-check/checks/services/gemini-cli.js +117 -0
- package/.sinapse-ai/core/health-check/checks/services/index.js +2 -0
- package/.sinapse-ai/core/health-check/healers/index.js +40 -3
- package/.sinapse-ai/core/ideation/ideation-engine.js +212 -107
- package/.sinapse-ai/core/ids/gate-evaluator.js +318 -0
- package/.sinapse-ai/core/ids/gates/g5-semantic-handshake.js +190 -0
- package/.sinapse-ai/core/ids/gates/g6-ci-integrity.js +162 -0
- package/.sinapse-ai/core/ids/index.js +30 -0
- package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +11 -0
- package/.sinapse-ai/core/memory/gotchas-memory.js +37 -2
- package/.sinapse-ai/core/orchestration/agent-invoker.js +29 -6
- package/.sinapse-ai/core/orchestration/brownfield-handler.js +36 -3
- package/.sinapse-ai/core/orchestration/condition-evaluator.js +57 -0
- package/.sinapse-ai/core/orchestration/executors/epic-3-executor.js +76 -5
- package/.sinapse-ai/core/orchestration/executors/epic-4-executor.js +63 -17
- package/.sinapse-ai/core/orchestration/executors/epic-6-executor.js +153 -41
- package/.sinapse-ai/core/orchestration/executors/epic-executor.js +40 -0
- package/.sinapse-ai/core/orchestration/greenfield-handler.js +87 -3
- package/.sinapse-ai/core/orchestration/master-orchestrator.js +150 -10
- package/.sinapse-ai/core/orchestration/parallel-executor.js +6 -1
- package/.sinapse-ai/core/orchestration/recovery-handler.js +81 -8
- package/.sinapse-ai/core/orchestration/workflow-executor.js +41 -0
- package/.sinapse-ai/core/registry/registry-loader.js +71 -5
- package/.sinapse-ai/core/registry/squad-agent-resolver.js +253 -0
- package/.sinapse-ai/core/synapse/context/context-tracker.js +104 -9
- package/.sinapse-ai/core/synapse/context/index.js +19 -0
- package/.sinapse-ai/core/synapse/context/semantic-handshake-engine.js +555 -0
- package/.sinapse-ai/core/synapse/diagnostics/collectors/pipeline-collector.js +4 -2
- package/.sinapse-ai/core/synapse/engine.js +43 -3
- package/.sinapse-ai/core/telemetry/ids-sink.js +188 -0
- package/.sinapse-ai/core/utils/output-formatter.js +8 -290
- package/.sinapse-ai/core/utils/spawn-safe.js +186 -0
- package/.sinapse-ai/core-config.yaml +68 -1
- package/.sinapse-ai/data/entity-registry.yaml +15082 -13618
- package/.sinapse-ai/data/registry-update-log.jsonl +143 -0
- package/.sinapse-ai/development/agents/developer.md +2 -0
- package/.sinapse-ai/development/agents/devops.md +9 -0
- package/.sinapse-ai/development/external-executors/README.md +18 -0
- package/.sinapse-ai/development/external-executors/codex.md +56 -0
- package/.sinapse-ai/development/scripts/populate-entity-registry.js +65 -9
- package/.sinapse-ai/development/scripts/squad/squad-downloader.js +169 -14
- package/.sinapse-ai/development/tasks/delegate-to-external-executor.md +152 -0
- package/.sinapse-ai/development/tasks/github-devops-pre-push-quality-gate.md +46 -29
- package/.sinapse-ai/development/tasks/update-sinapse.md +3 -3
- package/.sinapse-ai/hooks/sinapse-brand-grounding.cjs +4 -7
- package/.sinapse-ai/hooks/sinapse-ds-grounding.cjs +5 -8
- package/.sinapse-ai/hooks/sinapse-vault-grounding.cjs +6 -9
- package/.sinapse-ai/infrastructure/integrations/ai-providers/ai-provider-factory.js +4 -1
- package/.sinapse-ai/infrastructure/integrations/ai-providers/claude-provider.js +57 -55
- package/.sinapse-ai/infrastructure/integrations/pm-adapters/github-adapter.js +9 -7
- package/.sinapse-ai/infrastructure/scripts/ide-sync/gemini-commands.js +298 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/index.js +127 -6
- package/.sinapse-ai/infrastructure/scripts/ide-sync/persona-renderer.js +97 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/antigravity.js +121 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/cursor.js +119 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/github-copilot.js +191 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/kimi.js +448 -0
- package/.sinapse-ai/install-manifest.yaml +218 -114
- package/.sinapse-ai/product/templates/engine/renderer.js +20 -1
- package/.sinapse-ai/scripts/pm.sh +18 -6
- package/bin/cli.js +17 -0
- package/bin/commands/agents.js +96 -0
- package/bin/commands/doctor.js +15 -0
- package/bin/commands/ideate.js +129 -0
- package/bin/commands/uninstall.js +40 -0
- package/bin/postinstall.js +50 -4
- package/bin/sinapse.js +146 -2
- package/bin/utils/secret-scanner-core.js +253 -0
- package/bin/utils/staged-secret-scan.js +106 -40
- package/docs/framework/collaboration-autonomy-plan.md +18 -18
- package/docs/guides/parallel-workflow.md +6 -6
- package/package.json +22 -5
- package/packages/installer/src/installer/git-hooks-installer.js +384 -0
- package/packages/installer/src/installer/sinapse-ai-installer.js +16 -0
- package/packages/installer/src/wizard/ide-config-generator.js +23 -0
- package/packages/installer/src/wizard/validators.js +38 -1
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +5 -1
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +44 -22
- package/packages/installer/tests/unit/git-hooks-installer.test.js +262 -0
- package/scripts/eval-runner.js +422 -0
- package/scripts/generate-install-manifest.js +13 -9
- package/scripts/generate-synapse-runtime.js +51 -0
- package/scripts/regenerate-orqx-stubs.ps1 +6 -5
- package/scripts/validate-all.js +1 -0
- package/scripts/validate-evals.js +466 -0
- package/scripts/validate-schemas.js +539 -0
- package/scripts/validate-squad-orqx.js +9 -2
- package/squads/claude-code-mastery/knowledge-base/memory-systems-reference.md +1 -1
- package/squads/squad-brand/templates/client-delivery-template.md +1 -1
- package/squads/squad-content/knowledge-base/social-compression-framework.md +1 -1
- package/squads/squad-council/knowledge-base/brand-strategy-models.md +1 -1
- package/.sinapse-ai/development/scripts/elicitation-engine.js +0 -385
- package/.sinapse-ai/development/scripts/elicitation-session-manager.js +0 -300
- package/.sinapse-ai/development/tasks/test-validation-task.md +0 -172
- package/docs/chrome-brain-upgrade-plan.md +0 -624
- package/docs/constitution-compliance.md +0 -87
- package/docs/mega-upgrade-orchestration-plan.md +0 -71
- package/docs/research-synthesis-for-upgrade.md +0 -511
- package/docs/security-audit-report.md +0 -306
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Schema Validation GATE — Meta-validation + Kind coverage + Artifact validation
|
|
6
|
+
*
|
|
7
|
+
* Deterministic CI gate. fail-CLOSED. Three layers, all driven by the SAME
|
|
8
|
+
* TemplateValidator (.sinapse-ai/product/templates/engine/validator.js, v2.0.0)
|
|
9
|
+
* that the engine already uses at render time — this gate does NOT reimplement
|
|
10
|
+
* Ajv kind-aware validation, it reuses the validator's Ajv instance, custom
|
|
11
|
+
* formats, schema resolution, data extraction and error formatting.
|
|
12
|
+
*
|
|
13
|
+
* LAYERS:
|
|
14
|
+
* 1. META-VALIDATION (always runs — the value in a public CI with no artifacts):
|
|
15
|
+
* Discover EVERY *.schema.json in the repo (git ls-files, node_modules
|
|
16
|
+
* excluded) and compile each one with a fresh Ajv draft-07 instance
|
|
17
|
+
* (strict:false + ajv-formats + the validator's custom formats). A schema
|
|
18
|
+
* that does NOT compile (invalid JSON, broken $ref, invalid meta-schema)
|
|
19
|
+
* -> violation -> exit 1.
|
|
20
|
+
*
|
|
21
|
+
* 2. KIND COVERAGE: for the engine's known templateTypes (adr, dbdr, epic,
|
|
22
|
+
* pmdr, prd-v2, prd, story, task), reuse TemplateValidator.checkSchemas()
|
|
23
|
+
* and report found/missing. A known kind with no schema -> violation.
|
|
24
|
+
*
|
|
25
|
+
* 3. ARTIFACT VALIDATION (no-op clean when absent): kind -> glob by CONVENTION
|
|
26
|
+
* (story -> docs/stories/ ** / *.story.md, epic -> docs/epics/ ** / *.md,
|
|
27
|
+
* prd -> docs/prd/ ** / *.md). docs/stories and docs/epics are gitignored,
|
|
28
|
+
* so in public CI there are 0 artifacts -> reported as skipped, NOT a
|
|
29
|
+
* failure. When artifacts ARE present, extract data via
|
|
30
|
+
* TemplateValidator.extractDataFromMarkdown (or frontmatter parse) and
|
|
31
|
+
* validate via TemplateValidator.validate(data, kind); each error is
|
|
32
|
+
* reported with the instancePath that formatErrors() already produces.
|
|
33
|
+
* An invalid artifact -> exit 1.
|
|
34
|
+
*
|
|
35
|
+
* Why a SEPARATE Ajv for meta-validation (layer 1): the shared
|
|
36
|
+
* TemplateValidator.ajv has allErrors/verbose on and caches by $id; compiling
|
|
37
|
+
* arbitrary repo schemas (which may share or collide on $id, or be config /
|
|
38
|
+
* infra schemas unrelated to templates) into it would pollute that cache and
|
|
39
|
+
* mask the real "does this schema compile on its own?" question. Layer 1 asks
|
|
40
|
+
* exactly that question, in isolation, per schema.
|
|
41
|
+
*
|
|
42
|
+
* EXIT CODES:
|
|
43
|
+
* 0 -> everything valid (and artifacts, if any, all valid).
|
|
44
|
+
* 1 -> any schema fails to compile, OR a known kind has no schema, OR an
|
|
45
|
+
* artifact is invalid.
|
|
46
|
+
* 2 -> gate/internal error (cannot load the validator, git discovery fails
|
|
47
|
+
* with no fallback, etc).
|
|
48
|
+
*
|
|
49
|
+
* FLAGS:
|
|
50
|
+
* --artifacts <dir> Use <dir> as the artifact root instead of the repo root
|
|
51
|
+
* (used by the jest wrapper / for testing). Globs are
|
|
52
|
+
* resolved relative to this root.
|
|
53
|
+
* --quiet Suppress the per-schema OK lines; still prints the
|
|
54
|
+
* summary and every violation.
|
|
55
|
+
*
|
|
56
|
+
* Will be wired into package.json (NOT touched here) as:
|
|
57
|
+
* "validate:schemas": "node scripts/validate-schemas.js"
|
|
58
|
+
*
|
|
59
|
+
* Node puro, Windows-nativo, ZERO new dependency (ajv + ajv-formats already deps).
|
|
60
|
+
*
|
|
61
|
+
* @module scripts/validate-schemas
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
const fs = require('fs');
|
|
65
|
+
const path = require('path');
|
|
66
|
+
const { execFileSync } = require('child_process');
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Paths
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
73
|
+
const ENGINE_DIR = path.join(REPO_ROOT, '.sinapse-ai', 'product', 'templates', 'engine');
|
|
74
|
+
const SCHEMAS_DIR = path.join(ENGINE_DIR, 'schemas');
|
|
75
|
+
const VALIDATOR_PATH = path.join(ENGINE_DIR, 'validator.js');
|
|
76
|
+
|
|
77
|
+
// Engine's known template types (mirrors the schemas the engine ships).
|
|
78
|
+
const KNOWN_KINDS = ['adr', 'dbdr', 'epic', 'pmdr', 'prd-v2', 'prd', 'story', 'task'];
|
|
79
|
+
|
|
80
|
+
// Artifact resolution by CONVENTION (path/filename). Frontmatter has no
|
|
81
|
+
// apiVersion/kind, so the kind comes from where the file lives + its suffix.
|
|
82
|
+
// `dir` is relative to the artifact root; `suffix` matches filename endings.
|
|
83
|
+
const ARTIFACT_CONVENTIONS = [
|
|
84
|
+
{ kind: 'story', dir: path.join('docs', 'stories'), suffix: '.story.md' },
|
|
85
|
+
// Epic artifacts are machine docs validated against the Epic *template
|
|
86
|
+
// variables* schema (id ^EPIC-…$, owner, objective, stories). Use an explicit
|
|
87
|
+
// `.epic.md` suffix — mirroring `.story.md` — so prose planning docs
|
|
88
|
+
// (README.md, SESSION-HANDOFF.md, story files) under docs/epics/ are NOT
|
|
89
|
+
// misclassified as epic artifacts. Before this, every .md under docs/epics/
|
|
90
|
+
// (tracked since the epics/stories split) failed the gate.
|
|
91
|
+
{ kind: 'epic', dir: path.join('docs', 'epics'), suffix: '.epic.md' },
|
|
92
|
+
{ kind: 'prd', dir: path.join('docs', 'prd'), suffix: '.md' },
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// CLI args
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
function parseArgs(argv) {
|
|
100
|
+
const opts = { artifactsRoot: REPO_ROOT, quiet: false };
|
|
101
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
102
|
+
const a = argv[i];
|
|
103
|
+
if (a === '--quiet') {
|
|
104
|
+
opts.quiet = true;
|
|
105
|
+
} else if (a === '--artifacts') {
|
|
106
|
+
const next = argv[i + 1];
|
|
107
|
+
if (!next) {
|
|
108
|
+
process.stderr.write('[validate-schemas] --artifacts requires a directory argument.\n');
|
|
109
|
+
process.exit(2);
|
|
110
|
+
}
|
|
111
|
+
opts.artifactsRoot = path.resolve(next);
|
|
112
|
+
i += 1;
|
|
113
|
+
} else if (a.startsWith('--artifacts=')) {
|
|
114
|
+
opts.artifactsRoot = path.resolve(a.slice('--artifacts='.length));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return opts;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Violation collection
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
/** @type {Array<{file:string, message:string}>} */
|
|
125
|
+
const violations = [];
|
|
126
|
+
function addViolation(file, message) {
|
|
127
|
+
violations.push({ file, message });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function rel(p, root = REPO_ROOT) {
|
|
131
|
+
return path.relative(root, p).split(path.sep).join('/') || path.basename(p);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Load the engine's TemplateValidator (REUSE — do not reimplement Ajv).
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
let TemplateValidator;
|
|
139
|
+
try {
|
|
140
|
+
({ TemplateValidator } = require(VALIDATOR_PATH));
|
|
141
|
+
if (typeof TemplateValidator !== 'function') {
|
|
142
|
+
throw new Error('validator.js did not export a TemplateValidator class');
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
process.stderr.write(
|
|
146
|
+
'[validate-schemas] FATAL: could not load TemplateValidator from\n' +
|
|
147
|
+
` ${VALIDATOR_PATH}\n` +
|
|
148
|
+
` reason: ${err.message}\n`,
|
|
149
|
+
);
|
|
150
|
+
process.exit(2);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// One shared validator instance for kind-coverage + artifact validation. It
|
|
154
|
+
// resolves schemas from the engine schemas dir and carries the custom formats
|
|
155
|
+
// (semver, slug, story-id, epic-id) the engine schemas may rely on.
|
|
156
|
+
const validator = new TemplateValidator({ schemasDir: SCHEMAS_DIR });
|
|
157
|
+
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Ajv factory for LAYER 1 meta-validation (fresh, isolated, per schema).
|
|
160
|
+
// Mirrors the validator's own config (strict:false, ajv-formats, custom
|
|
161
|
+
// formats) so "compiles in the gate" == "compiles in the engine".
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
// Draft-07 is the default (matches the engine's TemplateValidator and the bulk
|
|
165
|
+
// of the repo). But the repo is heterogeneous: a schema may legitimately declare
|
|
166
|
+
// a NEWER meta-schema (e.g. draft 2020-12 in .codex/*.parity.schema.json).
|
|
167
|
+
// Forcing such a schema through the draft-07 compiler is a FALSE failure — it is
|
|
168
|
+
// valid, just newer. So the meta-validator is draft-AWARE: it picks the Ajv
|
|
169
|
+
// build by the schema's declared `$schema`. All builds ship INSIDE the `ajv`
|
|
170
|
+
// package (ajv, ajv/dist/2020, ajv/dist/2019) -> ZERO new dependency.
|
|
171
|
+
let AjvDraft07;
|
|
172
|
+
let Ajv2020;
|
|
173
|
+
let Ajv2019;
|
|
174
|
+
let addFormats;
|
|
175
|
+
try {
|
|
176
|
+
|
|
177
|
+
AjvDraft07 = require('ajv'); // draft-07 (also draft-06/04 via addMetaSchema, default here)
|
|
178
|
+
|
|
179
|
+
addFormats = require('ajv-formats');
|
|
180
|
+
} catch (err) {
|
|
181
|
+
process.stderr.write(
|
|
182
|
+
`[validate-schemas] FATAL: ajv / ajv-formats not available (expected as deps): ${err.message}\n`,
|
|
183
|
+
);
|
|
184
|
+
process.exit(2);
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
|
|
188
|
+
Ajv2020 = require('ajv/dist/2020');
|
|
189
|
+
} catch {
|
|
190
|
+
Ajv2020 = null; // graceful: a 2020-12 schema then fails with an actionable message
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
|
|
194
|
+
Ajv2019 = require('ajv/dist/2019');
|
|
195
|
+
} catch {
|
|
196
|
+
Ajv2019 = null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function registerCustomFormats(ajv) {
|
|
200
|
+
// Same custom formats the TemplateValidator registers — so a schema that
|
|
201
|
+
// uses "format": "semver" | "slug" | "story-id" | "epic-id" still compiles
|
|
202
|
+
// here (with strict:false an unknown format would not throw, but mirroring
|
|
203
|
+
// keeps the gate semantically identical to the engine).
|
|
204
|
+
ajv.addFormat('semver', { validate: (s) => /^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$/.test(s) });
|
|
205
|
+
ajv.addFormat('slug', { validate: (s) => /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(s) });
|
|
206
|
+
ajv.addFormat('story-id', { validate: (s) => /^\d+\.\d+$/.test(s) });
|
|
207
|
+
ajv.addFormat('epic-id', { validate: (s) => /^EPIC-[A-Z0-9]+$/.test(s) });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Resolve which Ajv constructor compiles a schema, from its `$schema` URI. */
|
|
211
|
+
function ajvCtorFor(schema) {
|
|
212
|
+
const meta = typeof schema.$schema === 'string' ? schema.$schema : '';
|
|
213
|
+
if (/2020-12/.test(meta)) return { ctor: Ajv2020, draft: 'draft 2020-12' };
|
|
214
|
+
if (/2019-09/.test(meta)) return { ctor: Ajv2019, draft: 'draft 2019-09' };
|
|
215
|
+
// draft-07 / draft-06 / draft-04 / unspecified -> default Ajv (draft-07).
|
|
216
|
+
return { ctor: AjvDraft07, draft: 'draft-07' };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function newAjvFor(schema) {
|
|
220
|
+
const { ctor, draft } = ajvCtorFor(schema);
|
|
221
|
+
if (!ctor) {
|
|
222
|
+
const err = new Error(
|
|
223
|
+
`meta-schema ${draft} requested but the matching Ajv build is unavailable in this environment`,
|
|
224
|
+
);
|
|
225
|
+
err.unsupportedDraft = true;
|
|
226
|
+
throw err;
|
|
227
|
+
}
|
|
228
|
+
const ajv = new ctor({ allErrors: true, strict: false, validateFormats: true });
|
|
229
|
+
addFormats(ajv);
|
|
230
|
+
registerCustomFormats(ajv);
|
|
231
|
+
return { ajv, draft };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
// Schema discovery — git ls-files (preferred), recursive readdir (fallback).
|
|
236
|
+
// The dot-prefixed dirs (.sinapse-ai, .codex) break a naive Glob, so we either
|
|
237
|
+
// ask git or walk the tree ourselves. node_modules is always excluded.
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
|
|
240
|
+
function discoverViaGit() {
|
|
241
|
+
try {
|
|
242
|
+
const out = execFileSync('git', ['ls-files', '*.schema.json'], {
|
|
243
|
+
cwd: REPO_ROOT,
|
|
244
|
+
encoding: 'utf8',
|
|
245
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
246
|
+
});
|
|
247
|
+
const files = out
|
|
248
|
+
.split('\n')
|
|
249
|
+
.map((l) => l.trim())
|
|
250
|
+
.filter(Boolean)
|
|
251
|
+
.filter((l) => !l.split('/').includes('node_modules'))
|
|
252
|
+
.map((l) => path.join(REPO_ROOT, l));
|
|
253
|
+
return files.length ? files : null;
|
|
254
|
+
} catch {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function discoverViaWalk(dir, acc) {
|
|
260
|
+
let entries;
|
|
261
|
+
try {
|
|
262
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
263
|
+
} catch {
|
|
264
|
+
return acc;
|
|
265
|
+
}
|
|
266
|
+
for (const e of entries) {
|
|
267
|
+
if (e.name === 'node_modules' || e.name === '.git') continue;
|
|
268
|
+
const full = path.join(dir, e.name);
|
|
269
|
+
if (e.isDirectory()) {
|
|
270
|
+
discoverViaWalk(full, acc);
|
|
271
|
+
} else if (e.isFile() && e.name.endsWith('.schema.json')) {
|
|
272
|
+
acc.push(full);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return acc;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function discoverSchemas() {
|
|
279
|
+
const viaGit = discoverViaGit();
|
|
280
|
+
if (viaGit) return { files: viaGit.sort(), method: 'git ls-files' };
|
|
281
|
+
const walked = discoverViaWalk(REPO_ROOT, []);
|
|
282
|
+
return { files: walked.sort(), method: 'recursive readdir' };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
// LAYER 1 — meta-validation: each schema compiles in isolation.
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
|
|
289
|
+
function metaValidate(schemaFiles, opts) {
|
|
290
|
+
let okCount = 0;
|
|
291
|
+
for (const file of schemaFiles) {
|
|
292
|
+
const relPath = rel(file);
|
|
293
|
+
let raw;
|
|
294
|
+
try {
|
|
295
|
+
raw = fs.readFileSync(file, 'utf8');
|
|
296
|
+
} catch (err) {
|
|
297
|
+
addViolation(relPath, `cannot read schema file: ${err.message}`);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
let schema;
|
|
301
|
+
try {
|
|
302
|
+
schema = JSON.parse(raw);
|
|
303
|
+
} catch (err) {
|
|
304
|
+
addViolation(relPath, `invalid JSON: ${err.message}`);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
// Fresh Ajv per schema -> no $id cache collisions across the repo's
|
|
308
|
+
// template / config / infra / quality / codex schemas. Draft-aware so a
|
|
309
|
+
// schema that declares a newer meta-schema is not a false failure.
|
|
310
|
+
try {
|
|
311
|
+
const { ajv, draft } = newAjvFor(schema);
|
|
312
|
+
ajv.compile(schema); // throws on broken $ref, invalid meta-schema, bad keywords
|
|
313
|
+
okCount += 1;
|
|
314
|
+
if (!opts.quiet) process.stdout.write(` ok ${relPath} (${draft})\n`);
|
|
315
|
+
} catch (err) {
|
|
316
|
+
addViolation(relPath, `does not compile: ${err.message}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return okCount;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
// LAYER 2 — kind coverage via the validator's own checkSchemas().
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
|
|
326
|
+
async function kindCoverage() {
|
|
327
|
+
const result = await validator.checkSchemas(KNOWN_KINDS);
|
|
328
|
+
for (const missing of result.missing) {
|
|
329
|
+
addViolation(
|
|
330
|
+
rel(SCHEMAS_DIR),
|
|
331
|
+
`known template kind '${missing}' has no schema (expected ${missing}.schema.json in ${rel(SCHEMAS_DIR)}/).`,
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
return result; // { complete, found, missing }
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
// LAYER 3 — artifact validation by convention (no-op clean when absent).
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
|
|
341
|
+
/** Recursively collect files under `dir` ending with `suffix`. */
|
|
342
|
+
function collectArtifacts(dir, suffix, acc) {
|
|
343
|
+
let entries;
|
|
344
|
+
try {
|
|
345
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
346
|
+
} catch {
|
|
347
|
+
return acc; // dir absent -> no-op
|
|
348
|
+
}
|
|
349
|
+
for (const e of entries) {
|
|
350
|
+
if (e.name === 'node_modules' || e.name === '.git') continue;
|
|
351
|
+
const full = path.join(dir, e.name);
|
|
352
|
+
if (e.isDirectory()) {
|
|
353
|
+
collectArtifacts(full, suffix, acc);
|
|
354
|
+
} else if (e.isFile() && e.name.endsWith(suffix)) {
|
|
355
|
+
acc.push(full);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return acc;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Minimal YAML frontmatter parser (--- ... ---). Engine artifacts have no
|
|
363
|
+
* apiVersion/kind, but a story/epic/prd may carry scalar + simple list/nested
|
|
364
|
+
* frontmatter. We parse what we safely can; deep structures fall back to the
|
|
365
|
+
* markdown-body extraction the validator already provides.
|
|
366
|
+
*/
|
|
367
|
+
function parseFrontmatter(content) {
|
|
368
|
+
const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
369
|
+
if (!m) return null;
|
|
370
|
+
const body = m[1];
|
|
371
|
+
const data = {};
|
|
372
|
+
const lines = body.split(/\r?\n/);
|
|
373
|
+
let pendingKey = null;
|
|
374
|
+
let pendingList = null;
|
|
375
|
+
for (const line of lines) {
|
|
376
|
+
if (line.trim() === '') continue;
|
|
377
|
+
const listItem = line.match(/^\s*-\s+(.*)$/);
|
|
378
|
+
if (listItem && pendingKey) {
|
|
379
|
+
if (!pendingList) {
|
|
380
|
+
pendingList = [];
|
|
381
|
+
data[pendingKey] = pendingList;
|
|
382
|
+
}
|
|
383
|
+
pendingList.push(coerce(listItem[1].trim()));
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const kv = line.match(/^([A-Za-z0-9_-]+)\s*:\s*(.*)$/);
|
|
387
|
+
if (kv) {
|
|
388
|
+
pendingKey = kv[1];
|
|
389
|
+
pendingList = null;
|
|
390
|
+
const val = kv[2].trim();
|
|
391
|
+
if (val === '') {
|
|
392
|
+
// could be a list/object on following lines
|
|
393
|
+
data[pendingKey] = '';
|
|
394
|
+
} else {
|
|
395
|
+
data[pendingKey] = coerce(val);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return data;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function coerce(v) {
|
|
403
|
+
if (/^-?\d+$/.test(v)) return parseInt(v, 10);
|
|
404
|
+
if (/^-?\d+\.\d+$/.test(v) && !/^\d+\.\d+$/.test(v)) return parseFloat(v); // avoid eating story ids like 3.6
|
|
405
|
+
if (v === 'true') return true;
|
|
406
|
+
if (v === 'false') return false;
|
|
407
|
+
// strip surrounding quotes
|
|
408
|
+
const q = v.match(/^["'](.*)["']$/);
|
|
409
|
+
return q ? q[1] : v;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Build the data object for an artifact. Prefer the validator's own extractor
|
|
414
|
+
* (markdown body `**Key:** value`), merged with frontmatter (frontmatter wins
|
|
415
|
+
* on key collision since it is the structured source).
|
|
416
|
+
*/
|
|
417
|
+
function extractArtifactData(content) {
|
|
418
|
+
let bodyData = {};
|
|
419
|
+
try {
|
|
420
|
+
bodyData = validator.extractDataFromMarkdown(content, {}) || {};
|
|
421
|
+
} catch {
|
|
422
|
+
bodyData = {};
|
|
423
|
+
}
|
|
424
|
+
const fm = parseFrontmatter(content) || {};
|
|
425
|
+
return { ...bodyData, ...fm };
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async function validateArtifacts(opts) {
|
|
429
|
+
const summary = { kinds: {}, total: 0, valid: 0, invalid: 0 };
|
|
430
|
+
|
|
431
|
+
for (const conv of ARTIFACT_CONVENTIONS) {
|
|
432
|
+
const root = path.join(opts.artifactsRoot, conv.dir);
|
|
433
|
+
const files = collectArtifacts(root, conv.suffix, []);
|
|
434
|
+
summary.kinds[conv.kind] = files.length;
|
|
435
|
+
summary.total += files.length;
|
|
436
|
+
|
|
437
|
+
for (const file of files) {
|
|
438
|
+
const relPath = rel(file, opts.artifactsRoot);
|
|
439
|
+
let content;
|
|
440
|
+
try {
|
|
441
|
+
content = fs.readFileSync(file, 'utf8');
|
|
442
|
+
} catch (err) {
|
|
443
|
+
addViolation(relPath, `cannot read artifact: ${err.message}`);
|
|
444
|
+
summary.invalid += 1;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
const data = extractArtifactData(content);
|
|
448
|
+
let result;
|
|
449
|
+
try {
|
|
450
|
+
// REUSE: kind-aware Ajv validate; errors already formatted as
|
|
451
|
+
// "<instancePath>: <message> (<details>)" by validator.formatErrors().
|
|
452
|
+
result = await validator.validate(data, conv.kind);
|
|
453
|
+
} catch (err) {
|
|
454
|
+
addViolation(relPath, `validation error for kind '${conv.kind}': ${err.message}`);
|
|
455
|
+
summary.invalid += 1;
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (result.isValid) {
|
|
459
|
+
summary.valid += 1;
|
|
460
|
+
if (!opts.quiet) process.stdout.write(` ok ${relPath} (${conv.kind})\n`);
|
|
461
|
+
} else {
|
|
462
|
+
summary.invalid += 1;
|
|
463
|
+
for (const e of result.errors) {
|
|
464
|
+
addViolation(relPath, `[${conv.kind}] ${e}`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return summary;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ---------------------------------------------------------------------------
|
|
473
|
+
// Main
|
|
474
|
+
// ---------------------------------------------------------------------------
|
|
475
|
+
|
|
476
|
+
async function main() {
|
|
477
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
478
|
+
|
|
479
|
+
// LAYER 1 — meta-validation (always).
|
|
480
|
+
const { files: schemaFiles, method } = discoverSchemas();
|
|
481
|
+
if (schemaFiles.length === 0) {
|
|
482
|
+
process.stderr.write(`[validate-schemas] FATAL: no *.schema.json discovered (method: ${method}).\n`);
|
|
483
|
+
process.exit(2);
|
|
484
|
+
}
|
|
485
|
+
if (!opts.quiet) process.stdout.write(`[validate-schemas] meta-validating ${schemaFiles.length} schema(s) (${method}):\n`);
|
|
486
|
+
const compiledOk = metaValidate(schemaFiles, opts);
|
|
487
|
+
|
|
488
|
+
// LAYER 2 — kind coverage.
|
|
489
|
+
const coverage = await kindCoverage();
|
|
490
|
+
|
|
491
|
+
// LAYER 3 — artifacts (no-op clean when absent).
|
|
492
|
+
const artifacts = await validateArtifacts(opts);
|
|
493
|
+
|
|
494
|
+
// -------------------------------------------------------------------------
|
|
495
|
+
// Summary
|
|
496
|
+
// -------------------------------------------------------------------------
|
|
497
|
+
process.stdout.write('\n[validate-schemas] summary\n');
|
|
498
|
+
process.stdout.write(
|
|
499
|
+
` schemas checked : ${schemaFiles.length} (${compiledOk} compiled OK, ${schemaFiles.length - compiledOk} failed)\n`,
|
|
500
|
+
);
|
|
501
|
+
process.stdout.write(
|
|
502
|
+
` kinds covered : ${coverage.found.length}/${KNOWN_KINDS.length} ` +
|
|
503
|
+
`(found: ${coverage.found.join(', ') || 'none'}` +
|
|
504
|
+
`${coverage.missing.length ? ` | missing: ${coverage.missing.join(', ')}` : ''})\n`,
|
|
505
|
+
);
|
|
506
|
+
if (artifacts.total === 0) {
|
|
507
|
+
process.stdout.write(' artifacts : 0 artifacts found (skipped)\n');
|
|
508
|
+
} else {
|
|
509
|
+
const perKind = Object.entries(artifacts.kinds)
|
|
510
|
+
.map(([k, n]) => `${k}:${n}`)
|
|
511
|
+
.join(', ');
|
|
512
|
+
process.stdout.write(
|
|
513
|
+
` artifacts : ${artifacts.total} validated (${artifacts.valid} valid, ${artifacts.invalid} invalid) [${perKind}]\n`,
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (violations.length === 0) {
|
|
518
|
+
process.stdout.write('\n[validate-schemas] OK — all schemas compile, all known kinds covered, all artifacts valid.\n');
|
|
519
|
+
process.exit(0);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
process.stdout.write(`\n[validate-schemas] FAIL — ${violations.length} violation(s):\n`);
|
|
523
|
+
const byFile = new Map();
|
|
524
|
+
for (const v of violations) {
|
|
525
|
+
if (!byFile.has(v.file)) byFile.set(v.file, []);
|
|
526
|
+
byFile.get(v.file).push(v.message);
|
|
527
|
+
}
|
|
528
|
+
for (const [file, msgs] of byFile) {
|
|
529
|
+
process.stdout.write(`\n ${file}\n`);
|
|
530
|
+
for (const m of msgs) process.stdout.write(` - ${m}\n`);
|
|
531
|
+
}
|
|
532
|
+
process.stdout.write('\n');
|
|
533
|
+
process.exit(1);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
main().catch((err) => {
|
|
537
|
+
process.stderr.write(`[validate-schemas] FATAL (uncaught): ${err && err.stack ? err.stack : err}\n`);
|
|
538
|
+
process.exit(2);
|
|
539
|
+
});
|
|
@@ -123,8 +123,14 @@ function parseOrqxFormat(content) {
|
|
|
123
123
|
const nameMatch = content.match(/\*\*Nome:\*\*\s*([^\n]+)/);
|
|
124
124
|
if (nameMatch) data.agent.name = nameMatch[1].trim();
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
// Accept "## Role" or "## Papel" (PT-BR alias)
|
|
127
|
+
const roleHeadingMatch = content.match(/^##\s+(?:Role|Papel)\s*\n+([\s\S]+?)(?:\n##|\n$)/m);
|
|
127
128
|
if (roleHeadingMatch) data.persona.role = roleHeadingMatch[1].trim().split('\n')[0];
|
|
129
|
+
// Inline role in list item (e.g. "- **Role:** Product Operations Orchestrator")
|
|
130
|
+
if (!data.persona.role) {
|
|
131
|
+
const inlineRoleMatch = content.match(/\*\*(?:Role|Papel):\*\*\s*([^\n]+)/);
|
|
132
|
+
if (inlineRoleMatch) data.persona.role = inlineRoleMatch[1].trim();
|
|
133
|
+
}
|
|
128
134
|
|
|
129
135
|
return data;
|
|
130
136
|
}
|
|
@@ -141,7 +147,8 @@ function parseOrqxFormat(content) {
|
|
|
141
147
|
const idMatch = content.match(/\*\*Agent ID\*\*\s*\|\s*`?@?([^|`\n]+?)`?\s*\|/);
|
|
142
148
|
if (idMatch) data.agent.id = idMatch[1].trim();
|
|
143
149
|
|
|
144
|
-
|
|
150
|
+
// Accept "## Role" or "## Papel" (PT-BR alias)
|
|
151
|
+
const roleHeadingMatch = content.match(/^##\s+(?:Role|Papel)\s*\n+([\s\S]+?)(?:\n##|\n$)/m);
|
|
145
152
|
if (roleHeadingMatch) data.persona.role = roleHeadingMatch[1].trim().split('\n')[0];
|
|
146
153
|
|
|
147
154
|
return data;
|
|
@@ -339,7 +339,7 @@ class AgentMemory:
|
|
|
339
339
|
```python
|
|
340
340
|
# Track how facts change over time
|
|
341
341
|
fact = KnowledgeFact(
|
|
342
|
-
content="User works at
|
|
342
|
+
content="User works at Acme Corp",
|
|
343
343
|
valid_from="2025-01",
|
|
344
344
|
valid_to=None, # Still valid
|
|
345
345
|
ingested_at="2025-01-15",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Slide 1: Capa
|
|
8
|
-
{Logo do projeto + "Entrega {N}: {phase_name}" + data +
|
|
8
|
+
{Logo do projeto + "Entrega {N}: {phase_name}" + data + {{your_brand}} logo}
|
|
9
9
|
|
|
10
10
|
## Slide 2: Agenda
|
|
11
11
|
{Lista dos topicos que serao apresentados nesta entrega}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Social Compression Framework — Compressao de Tese Densa em Conteudo Social-Native
|
|
2
2
|
|
|
3
|
-
> Fonte:
|
|
3
|
+
> Fonte: padrões de posicionamento de alta densidade — embalagem social-native e retencao.
|
|
4
4
|
|
|
5
5
|
## Principio Central
|
|
6
6
|
|
|
@@ -173,7 +173,7 @@ Frameworks para construcao, gestao e posicionamento de marca. Da identidade ao v
|
|
|
173
173
|
- **Network Effect**: Conecte pessoas de forma generosa — voce se torna o hub de conexoes
|
|
174
174
|
|
|
175
175
|
**Ferramentas**: LinkedIn (B2B), Twitter/X (tech/thought leadership), YouTube (profundidade), Substack/Newsletter (ownership), Podcast (intimidade)
|
|
176
|
-
- **Example Application**:
|
|
176
|
+
- **Example Application**: a solo founder (boutique brand studio context): Specific Knowledge = intersection de IA + branding + storytelling. Public: publicar casos de uso, frameworks proprios, resultados dos clientes. Leverage: SINAPSE como produto que escala o conhecimento.
|
|
177
177
|
- **Key Metrics**: Inbound leads via personal brand; speaking invites; colaboracoes inbound; preco hora premium vs. mercado
|
|
178
178
|
- **Cross-References**: Productize Yourself (Naval), Golden Circle (Sinek), Specific Knowledge Audit
|
|
179
179
|
|