create-snipara 2.0.14 → 2.0.16
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/README.md +49 -4
- package/dist/index.js +603 -259
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Use it for:
|
|
|
29
29
|
- free account signup with reviewed Context + Memory
|
|
30
30
|
- local project wiring, `doctor`, `repair`, and `upgrade`
|
|
31
31
|
- local source activation without GitHub, plus optional GitHub repository sync and PR Answer Packs approval when the current folder is a GitHub repo
|
|
32
|
-
- day-zero
|
|
32
|
+
- day-zero proof with a ready-to-ask grounded question, a small hosted README/docs seed sync, and an evidence-linked Project Bootstrap for new projects
|
|
33
33
|
|
|
34
34
|
## Why It Exists
|
|
35
35
|
|
|
@@ -137,6 +137,47 @@ That distinction matters:
|
|
|
137
137
|
- optionally installs Claude Code agent hooks and companion local code overlay Git hooks
|
|
138
138
|
- supports `doctor`, `repair`, `upgrade`, and `print-config`
|
|
139
139
|
|
|
140
|
+
## Project Bootstrap: Cold-Start Intelligence
|
|
141
|
+
|
|
142
|
+
During setup, `create-snipara` builds a reviewable picture of the repository
|
|
143
|
+
before the first agent task. It writes both a machine-readable artifact and a
|
|
144
|
+
human-readable report:
|
|
145
|
+
|
|
146
|
+
- `.snipara/activation/project-bootstrap.json`
|
|
147
|
+
- `.snipara/activation/project-bootstrap.md`
|
|
148
|
+
|
|
149
|
+
The report summarizes eight dynamic sections. Counts come from the current
|
|
150
|
+
repository; they are not fixed demo numbers:
|
|
151
|
+
|
|
152
|
+
- architecture patterns
|
|
153
|
+
- conventions
|
|
154
|
+
- decision candidates
|
|
155
|
+
- procedures
|
|
156
|
+
- skills and candidate workflows
|
|
157
|
+
- recurring failure candidates
|
|
158
|
+
- critical code symbols
|
|
159
|
+
- verification patterns
|
|
160
|
+
|
|
161
|
+
The local pass uses bounded evidence from Git history, `AGENTS.md`,
|
|
162
|
+
`CLAUDE.md`, `README`, `docs/`, CI configuration, package scripts, test
|
|
163
|
+
patterns, and release history. Every item links back to its evidence. Directly
|
|
164
|
+
detected facts can be marked `observed`; inferred decisions, workflows, and
|
|
165
|
+
failure patterns remain `needs_review` and are not promoted to durable project
|
|
166
|
+
memory automatically.
|
|
167
|
+
|
|
168
|
+
The artifact also includes a coverage ledger. PR and issue history are marked
|
|
169
|
+
`unavailable` until the user explicitly connects a source provider. Critical
|
|
170
|
+
symbols are marked `deferred` until Code Graph indexing is available. Snipara
|
|
171
|
+
does not invent candidates to make these sections look complete.
|
|
172
|
+
|
|
173
|
+
After setup, start here:
|
|
174
|
+
|
|
175
|
+
> Review what Snipara learned about your project.
|
|
176
|
+
|
|
177
|
+
The Project Bootstrap summary is also carried into the First Work Brief so the
|
|
178
|
+
agent begins with the repository's inherited experience and its remaining
|
|
179
|
+
evidence gaps in view.
|
|
180
|
+
|
|
140
181
|
## Product Boundary
|
|
141
182
|
|
|
142
183
|
Keep the package roles simple:
|
|
@@ -404,7 +445,7 @@ The intended split is:
|
|
|
404
445
|
|
|
405
446
|
- `create-snipara`: Context + Memory onboarding, auth, config, inspect, repair, and upgrade
|
|
406
447
|
- `snipara-openclaw-install`: OpenClaw-only setup wrapper that remains narrower than `create-snipara`
|
|
407
|
-
- `snipara-companion`:
|
|
448
|
+
- `snipara-companion`: optional local workflow helper after first value
|
|
408
449
|
- `snipara-mcp`: local stdio bridge, client-package development, compatibility, and testing
|
|
409
450
|
- `snipara-sandbox`: optional local execution engine
|
|
410
451
|
|
|
@@ -416,8 +457,8 @@ companion add workflow ergonomics:
|
|
|
416
457
|
- use Hosted MCP as the core Snipara surface
|
|
417
458
|
- use `AGENTS.md` for project instructions
|
|
418
459
|
- use the selected pack under `.snipara/companion/`, for example `CODEX.md`, `CURSOR.md`, or `GEMINI.md`
|
|
419
|
-
-
|
|
420
|
-
- choose `hosted-
|
|
460
|
+
- start with the default `hosted-only` path for the shortest route to a grounded answer
|
|
461
|
+
- choose `hosted-companion` after first value when you want compaction-safe local phase commits
|
|
421
462
|
- use local `snipara-mcp` only for stdio compatibility, development, or tests
|
|
422
463
|
|
|
423
464
|
Claude Code, Cursor, and Kimi Code CLI can receive generated automation files when
|
|
@@ -461,6 +502,10 @@ After setup, the project gets a local pack at `.snipara/companion/`:
|
|
|
461
502
|
- `commands.json`: starter commands for the current project
|
|
462
503
|
- `doctor.json`: most recent validation report
|
|
463
504
|
|
|
505
|
+
Activation evidence is kept separately under `.snipara/activation/`, including
|
|
506
|
+
the Project Bootstrap JSON/Markdown pair, First Work Brief, handoff, and memory
|
|
507
|
+
candidate review artifacts.
|
|
508
|
+
|
|
464
509
|
This pack is meant to be the local source of truth for machine-level setup. When
|
|
465
510
|
`snipara-companion` is installed, it includes business-folder onboarding commands
|
|
466
511
|
such as:
|
package/dist/index.js
CHANGED
|
@@ -13,8 +13,8 @@ import { homedir } from "os";
|
|
|
13
13
|
// package.json
|
|
14
14
|
var package_default = {
|
|
15
15
|
name: "create-snipara",
|
|
16
|
-
version: "2.0.
|
|
17
|
-
description: "Onboard Snipara Hosted MCP Context + Memory with
|
|
16
|
+
version: "2.0.16",
|
|
17
|
+
description: "Onboard Snipara Hosted MCP Context + Memory with project bootstrap intelligence and optional local workflows",
|
|
18
18
|
main: "dist/index.js",
|
|
19
19
|
type: "module",
|
|
20
20
|
bin: {
|
|
@@ -23,7 +23,7 @@ var package_default = {
|
|
|
23
23
|
scripts: {
|
|
24
24
|
build: "tsup src/index.ts --format esm --dts --clean",
|
|
25
25
|
dev: "tsup src/index.ts --format esm --watch",
|
|
26
|
-
|
|
26
|
+
prepack: "npm run build",
|
|
27
27
|
pretest: "pnpm build",
|
|
28
28
|
test: "node --test test/*.test.cjs",
|
|
29
29
|
"pack:smoke": "bash ../../scripts/smoke-node-package.sh packages/create-snipara create-snipara create-snipara --help",
|
|
@@ -107,6 +107,9 @@ var DEVICE_CLIENT_ID = "create-snipara";
|
|
|
107
107
|
var DAY_ZERO_CORPUS_MAX_FILES = 8;
|
|
108
108
|
var DAY_ZERO_CORPUS_MAX_FILE_CHARS = 12e3;
|
|
109
109
|
var DAY_ZERO_CORPUS_MAX_TOTAL_CHARS = 5e4;
|
|
110
|
+
var PROJECT_BOOTSTRAP_SCHEMA_VERSION = "snipara.project_bootstrap.v1";
|
|
111
|
+
var PROJECT_BOOTSTRAP_MAX_WALKED_FILES = 2500;
|
|
112
|
+
var PROJECT_BOOTSTRAP_MAX_MATCHED_FILES = 80;
|
|
110
113
|
var ACTIVATION_MANIFEST_SCHEMA_VERSION = "snipara.activation.manifest.v1";
|
|
111
114
|
var ACTIVATION_CONSUMER_CONTRACT_VERSION = "snipara.editor.activation.v1";
|
|
112
115
|
var EXPERIENCE_GOAL_SECTION = `## Experience Goal
|
|
@@ -154,6 +157,12 @@ function getFirstWorkBriefPath() {
|
|
|
154
157
|
function getMemoryCandidatesPath() {
|
|
155
158
|
return path.join(getActivationDir(), "memory-candidates.json");
|
|
156
159
|
}
|
|
160
|
+
function getProjectBootstrapJsonPath() {
|
|
161
|
+
return path.join(getActivationDir(), "project-bootstrap.json");
|
|
162
|
+
}
|
|
163
|
+
function getProjectBootstrapMarkdownPath() {
|
|
164
|
+
return path.join(getActivationDir(), "project-bootstrap.md");
|
|
165
|
+
}
|
|
157
166
|
function writeActivationManifest(manifest) {
|
|
158
167
|
fs.mkdirSync(getActivationDir(), { recursive: true });
|
|
159
168
|
const payload = JSON.stringify(manifest, null, 2) + "\n";
|
|
@@ -561,6 +570,23 @@ function listFilesUnder(relativeDir, maxFiles) {
|
|
|
561
570
|
visit(root);
|
|
562
571
|
return results;
|
|
563
572
|
}
|
|
573
|
+
function isCreateSniparaGeneratedInstructionSource(relativePath) {
|
|
574
|
+
if (!["AGENTS.md", "CLAUDE.md", ".cursor/rules/snipara.mdc"].includes(relativePath)) {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
try {
|
|
578
|
+
const prefix = fs.readFileSync(path.join(process.cwd(), relativePath), "utf8").slice(0, 4096);
|
|
579
|
+
return prefix.includes(
|
|
580
|
+
"## Snipara Context Workflow\n\nThis project uses Snipara Hosted MCP for project context and reviewed memory."
|
|
581
|
+
) || prefix.includes(
|
|
582
|
+
"Canonical project context is in `AGENTS.md`. This file exists for Claude Code compatibility."
|
|
583
|
+
) || prefix.includes(
|
|
584
|
+
"description: Use Snipara Hosted MCP for project context and reviewed memory."
|
|
585
|
+
);
|
|
586
|
+
} catch {
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
564
590
|
function findDecisionArchaeologySources(cwd) {
|
|
565
591
|
const exists = (relativePath) => fs.existsSync(path.join(cwd, relativePath));
|
|
566
592
|
const directSources = [
|
|
@@ -578,7 +604,528 @@ function findDecisionArchaeologySources(cwd) {
|
|
|
578
604
|
...listFilesUnder("docs/decisions", 20),
|
|
579
605
|
...listFilesUnder(".cursor/rules", 20)
|
|
580
606
|
];
|
|
581
|
-
return [.../* @__PURE__ */ new Set([...directSources, ...archaeologySources])].slice(0, 80);
|
|
607
|
+
return [.../* @__PURE__ */ new Set([...directSources, ...archaeologySources])].filter((source) => !isCreateSniparaGeneratedInstructionSource(source)).slice(0, 80);
|
|
608
|
+
}
|
|
609
|
+
function findBoundedProjectFiles(predicate, maxMatches = PROJECT_BOOTSTRAP_MAX_MATCHED_FILES) {
|
|
610
|
+
const ignoredDirectories = /* @__PURE__ */ new Set([
|
|
611
|
+
".git",
|
|
612
|
+
".next",
|
|
613
|
+
".snipara",
|
|
614
|
+
".turbo",
|
|
615
|
+
".venv",
|
|
616
|
+
"build",
|
|
617
|
+
"coverage",
|
|
618
|
+
"dist",
|
|
619
|
+
"node_modules",
|
|
620
|
+
"target",
|
|
621
|
+
"vendor"
|
|
622
|
+
]);
|
|
623
|
+
const results = [];
|
|
624
|
+
let walkedFiles = 0;
|
|
625
|
+
const visit = (directory) => {
|
|
626
|
+
if (walkedFiles >= PROJECT_BOOTSTRAP_MAX_WALKED_FILES || results.length >= maxMatches) {
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
let entries;
|
|
630
|
+
try {
|
|
631
|
+
entries = fs.readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
632
|
+
} catch {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
for (const entry of entries) {
|
|
636
|
+
if (entry.isSymbolicLink()) {
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
const absolutePath = path.join(directory, entry.name);
|
|
640
|
+
if (entry.isDirectory()) {
|
|
641
|
+
if (!ignoredDirectories.has(entry.name)) {
|
|
642
|
+
visit(absolutePath);
|
|
643
|
+
}
|
|
644
|
+
} else if (entry.isFile()) {
|
|
645
|
+
walkedFiles += 1;
|
|
646
|
+
const relativePath = path.relative(process.cwd(), absolutePath).replace(/\\/g, "/");
|
|
647
|
+
if (predicate(relativePath)) {
|
|
648
|
+
results.push(relativePath);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (walkedFiles >= PROJECT_BOOTSTRAP_MAX_WALKED_FILES || results.length >= maxMatches) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
visit(process.cwd());
|
|
657
|
+
return results;
|
|
658
|
+
}
|
|
659
|
+
function readProjectPackageMetadata() {
|
|
660
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
661
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
662
|
+
return { scripts: {}, dependencies: [], workspaces: false };
|
|
663
|
+
}
|
|
664
|
+
try {
|
|
665
|
+
const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
666
|
+
const scripts = Object.fromEntries(
|
|
667
|
+
Object.entries(parsed.scripts ?? {}).filter(
|
|
668
|
+
(entry) => typeof entry[1] === "string"
|
|
669
|
+
)
|
|
670
|
+
);
|
|
671
|
+
const dependencies = [
|
|
672
|
+
...Object.keys(parsed.dependencies ?? {}),
|
|
673
|
+
...Object.keys(parsed.devDependencies ?? {}),
|
|
674
|
+
...Object.keys(parsed.peerDependencies ?? {})
|
|
675
|
+
];
|
|
676
|
+
return {
|
|
677
|
+
scripts,
|
|
678
|
+
dependencies: [...new Set(dependencies)].sort(),
|
|
679
|
+
workspaces: Boolean(parsed.workspaces)
|
|
680
|
+
};
|
|
681
|
+
} catch {
|
|
682
|
+
return { scripts: {}, dependencies: [], workspaces: false };
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
function readBootstrapGitSubjects(maxCount) {
|
|
686
|
+
try {
|
|
687
|
+
const output = execSync(`git log --pretty=format:%h%x20%s --max-count=${maxCount}`, {
|
|
688
|
+
cwd: process.cwd(),
|
|
689
|
+
encoding: "utf-8",
|
|
690
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
691
|
+
timeout: 1500
|
|
692
|
+
}).trim();
|
|
693
|
+
return output ? output.split(/\r?\n/).map((line) => line.trim()) : [];
|
|
694
|
+
} catch {
|
|
695
|
+
return [];
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
function readBootstrapReleaseTags() {
|
|
699
|
+
try {
|
|
700
|
+
const output = execSync("git tag --sort=-creatordate", {
|
|
701
|
+
cwd: process.cwd(),
|
|
702
|
+
encoding: "utf-8",
|
|
703
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
704
|
+
timeout: 1e3
|
|
705
|
+
}).trim();
|
|
706
|
+
return output ? output.split(/\r?\n/).slice(0, 20) : [];
|
|
707
|
+
} catch {
|
|
708
|
+
return [];
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
function createBootstrapItem(section, title, status, detail, evidence) {
|
|
712
|
+
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 56);
|
|
713
|
+
return {
|
|
714
|
+
id: `${section}:${slug || "candidate"}`,
|
|
715
|
+
title,
|
|
716
|
+
status,
|
|
717
|
+
detail,
|
|
718
|
+
evidence
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function buildProjectBootstrap(scan) {
|
|
722
|
+
const cwd = process.cwd();
|
|
723
|
+
const exists = (relativePath) => fs.existsSync(path.join(cwd, relativePath));
|
|
724
|
+
const packageMetadata = readProjectPackageMetadata();
|
|
725
|
+
const allDetectedFiles = findBoundedProjectFiles(() => true, PROJECT_BOOTSTRAP_MAX_WALKED_FILES);
|
|
726
|
+
const testFiles = allDetectedFiles.filter(
|
|
727
|
+
(relativePath) => /(^|\/)(?:__tests__|tests?|spec)(\/|$)|(?:^|\/)[^/]+\.(?:test|spec)\.[^/]+$|(?:^|\/)test_[^/]+\.py$/i.test(
|
|
728
|
+
relativePath
|
|
729
|
+
)
|
|
730
|
+
);
|
|
731
|
+
const skillFiles = allDetectedFiles.filter(
|
|
732
|
+
(relativePath) => /(^|\/)SKILL\.md$/i.test(relativePath)
|
|
733
|
+
);
|
|
734
|
+
const ciFiles = [
|
|
735
|
+
...allDetectedFiles.filter(
|
|
736
|
+
(relativePath) => /^\.github\/workflows\/[^/]+\.ya?ml$/i.test(relativePath)
|
|
737
|
+
),
|
|
738
|
+
...[".gitlab-ci.yml", "azure-pipelines.yml", "Jenkinsfile", ".circleci/config.yml"].filter(
|
|
739
|
+
exists
|
|
740
|
+
)
|
|
741
|
+
].filter((value, index, values) => values.indexOf(value) === index);
|
|
742
|
+
const instructionSources = scan.files.candidateDocs.filter(
|
|
743
|
+
(source) => classifyDecisionSource(source) === "agent_instruction"
|
|
744
|
+
);
|
|
745
|
+
const documentationSources = [
|
|
746
|
+
...["README.md", "CHANGELOG.md", "MEMORY.md"].filter(exists),
|
|
747
|
+
...listFilesUnder("docs", 40)
|
|
748
|
+
].filter((value, index, values) => values.indexOf(value) === index);
|
|
749
|
+
const releaseTags = scan.git.detected ? readBootstrapReleaseTags() : [];
|
|
750
|
+
const gitSubjects = scan.git.detected ? readBootstrapGitSubjects(120) : [];
|
|
751
|
+
const architecture = [];
|
|
752
|
+
if (scan.languages.length > 0) {
|
|
753
|
+
architecture.push(
|
|
754
|
+
createBootstrapItem(
|
|
755
|
+
"architecture",
|
|
756
|
+
`Languages: ${scan.languages.join(", ")}`,
|
|
757
|
+
"observed",
|
|
758
|
+
"Detected from root manifests and source extensions.",
|
|
759
|
+
scan.files.hasPackageJson ? [{ source: "package.json", kind: "manifest" }] : [{ source: ".", kind: "filesystem_scan" }]
|
|
760
|
+
)
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
if (scan.packageManagers.length > 0) {
|
|
764
|
+
architecture.push(
|
|
765
|
+
createBootstrapItem(
|
|
766
|
+
"architecture",
|
|
767
|
+
`Package manager: ${scan.packageManagers.join(", ")}`,
|
|
768
|
+
"observed",
|
|
769
|
+
"Detected from lockfiles.",
|
|
770
|
+
scan.files.lockfiles.map((source) => ({ source, kind: "lockfile" }))
|
|
771
|
+
)
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
const workspaceEvidence = ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json"].filter(
|
|
775
|
+
exists
|
|
776
|
+
);
|
|
777
|
+
if (packageMetadata.workspaces || workspaceEvidence.length > 0) {
|
|
778
|
+
architecture.push(
|
|
779
|
+
createBootstrapItem(
|
|
780
|
+
"architecture",
|
|
781
|
+
"Workspace or monorepo layout",
|
|
782
|
+
"observed",
|
|
783
|
+
"A workspace declaration or workspace configuration file is present.",
|
|
784
|
+
(workspaceEvidence.length > 0 ? workspaceEvidence : ["package.json"]).map((source) => ({
|
|
785
|
+
source,
|
|
786
|
+
kind: "config"
|
|
787
|
+
}))
|
|
788
|
+
)
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
for (const directory of ["apps", "packages", "services", "libs"].filter(exists)) {
|
|
792
|
+
architecture.push(
|
|
793
|
+
createBootstrapItem(
|
|
794
|
+
"architecture",
|
|
795
|
+
`${directory}/ project boundary`,
|
|
796
|
+
"observed",
|
|
797
|
+
"Top-level project organization detected; no architectural meaning was inferred beyond its presence.",
|
|
798
|
+
[{ source: `${directory}/`, kind: "directory" }]
|
|
799
|
+
)
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
const frameworks = [
|
|
803
|
+
["next", "Next.js"],
|
|
804
|
+
["react", "React"],
|
|
805
|
+
["@nestjs/core", "NestJS"],
|
|
806
|
+
["express", "Express"],
|
|
807
|
+
["fastify", "Fastify"],
|
|
808
|
+
["vitest", "Vitest"],
|
|
809
|
+
["jest", "Jest"]
|
|
810
|
+
];
|
|
811
|
+
for (const [dependency, label] of frameworks) {
|
|
812
|
+
if (packageMetadata.dependencies.includes(dependency)) {
|
|
813
|
+
architecture.push(
|
|
814
|
+
createBootstrapItem(
|
|
815
|
+
"architecture",
|
|
816
|
+
`${label} dependency`,
|
|
817
|
+
"observed",
|
|
818
|
+
`The root manifest declares ${dependency}; its role still needs code-level confirmation.`,
|
|
819
|
+
[{ source: "package.json", kind: "manifest" }]
|
|
820
|
+
)
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
if (scan.files.hasDockerfile) {
|
|
825
|
+
architecture.push(
|
|
826
|
+
createBootstrapItem(
|
|
827
|
+
"architecture",
|
|
828
|
+
"Container build surface",
|
|
829
|
+
"observed",
|
|
830
|
+
"A root Dockerfile is present.",
|
|
831
|
+
[{ source: "Dockerfile", kind: "config" }]
|
|
832
|
+
)
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
const candidates = buildMemoryCandidatePreviews(scan);
|
|
836
|
+
const conventions = candidates.filter(
|
|
837
|
+
(candidate) => candidate.evidence === "agent_instruction" || candidate.evidence === "project_document"
|
|
838
|
+
).slice(0, 16).map(
|
|
839
|
+
(candidate) => createBootstrapItem(
|
|
840
|
+
"conventions",
|
|
841
|
+
candidate.text.slice(0, 100),
|
|
842
|
+
"needs_review",
|
|
843
|
+
candidate.reason,
|
|
844
|
+
[{ source: candidate.source, kind: candidate.evidence }]
|
|
845
|
+
)
|
|
846
|
+
);
|
|
847
|
+
const decisions = candidates.filter(
|
|
848
|
+
(candidate) => ["git_commit_subject", "adr_or_decision_doc", "changelog"].includes(candidate.evidence)
|
|
849
|
+
).slice(0, 12).map(
|
|
850
|
+
(candidate) => createBootstrapItem(
|
|
851
|
+
"decisions",
|
|
852
|
+
candidate.text.slice(0, 100),
|
|
853
|
+
"needs_review",
|
|
854
|
+
candidate.reason,
|
|
855
|
+
[{ source: candidate.source, kind: candidate.evidence }]
|
|
856
|
+
)
|
|
857
|
+
);
|
|
858
|
+
const procedureScriptPattern = /(^|:)(build|dev|start|lint|test|type-?check|check|verify|deploy|release|migrate|seed|smoke|e2e)(:|$)/i;
|
|
859
|
+
const procedures = Object.keys(packageMetadata.scripts).filter((name) => procedureScriptPattern.test(name)).slice(0, 20).map(
|
|
860
|
+
(name) => createBootstrapItem(
|
|
861
|
+
"procedures",
|
|
862
|
+
`package script: ${name}`,
|
|
863
|
+
"observed",
|
|
864
|
+
"A root package script is configured; its exact command remains in package.json.",
|
|
865
|
+
[{ source: "package.json", kind: "package_script" }]
|
|
866
|
+
)
|
|
867
|
+
);
|
|
868
|
+
for (const source of ciFiles.slice(0, 8)) {
|
|
869
|
+
procedures.push(
|
|
870
|
+
createBootstrapItem(
|
|
871
|
+
"procedures",
|
|
872
|
+
`CI workflow: ${path.basename(source)}`,
|
|
873
|
+
"observed",
|
|
874
|
+
"Automation configuration detected; individual jobs were not interpreted during the bounded scan.",
|
|
875
|
+
[{ source, kind: "ci_workflow" }]
|
|
876
|
+
)
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
const skills = skillFiles.slice(0, 12).map(
|
|
880
|
+
(source) => createBootstrapItem(
|
|
881
|
+
"skills",
|
|
882
|
+
`Existing skill: ${path.basename(path.dirname(source)) || "root"}`,
|
|
883
|
+
"observed",
|
|
884
|
+
"An explicit SKILL.md workflow was found.",
|
|
885
|
+
[{ source, kind: "skill_definition" }]
|
|
886
|
+
)
|
|
887
|
+
);
|
|
888
|
+
const verificationScriptNames = Object.keys(packageMetadata.scripts).filter(
|
|
889
|
+
(name) => /test|lint|type|check|verify|smoke|e2e|build/i.test(name)
|
|
890
|
+
);
|
|
891
|
+
if (verificationScriptNames.length >= 2 && skills.length < 12) {
|
|
892
|
+
skills.push(
|
|
893
|
+
createBootstrapItem(
|
|
894
|
+
"skills",
|
|
895
|
+
"Verification workflow candidate",
|
|
896
|
+
"needs_review",
|
|
897
|
+
`Related scripts detected: ${verificationScriptNames.slice(0, 8).join(", ")}. Review their intended order before promoting this to a skill.`,
|
|
898
|
+
[{ source: "package.json", kind: "package_scripts" }]
|
|
899
|
+
)
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
const failureTerms = [
|
|
903
|
+
["rollback or revert", /\b(?:rollback|revert)\b/i],
|
|
904
|
+
["hotfix", /\bhotfix\b/i],
|
|
905
|
+
["regression", /\bregression\b/i],
|
|
906
|
+
["flaky verification", /\b(?:flaky|flakiness)\b/i],
|
|
907
|
+
["broken or failing behavior", /\b(?:broken|failing|failure)\b/i]
|
|
908
|
+
];
|
|
909
|
+
const recurringFailures = failureTerms.flatMap(([label, matcher]) => {
|
|
910
|
+
const matches = gitSubjects.filter((subject) => matcher.test(subject)).slice(0, 6);
|
|
911
|
+
if (matches.length < 2) {
|
|
912
|
+
return [];
|
|
913
|
+
}
|
|
914
|
+
return [
|
|
915
|
+
createBootstrapItem(
|
|
916
|
+
"recurringFailures",
|
|
917
|
+
`${label} signal (${matches.length} commits)`,
|
|
918
|
+
"needs_review",
|
|
919
|
+
"Repeated commit-language is a signal, not proof of a common root cause. Review the linked commits before accepting this anti-pattern.",
|
|
920
|
+
matches.map((source) => ({ source, kind: "git_commit_subject" }))
|
|
921
|
+
)
|
|
922
|
+
];
|
|
923
|
+
});
|
|
924
|
+
const verificationPatterns = verificationScriptNames.slice(0, 16).map(
|
|
925
|
+
(name) => createBootstrapItem(
|
|
926
|
+
"verificationPatterns",
|
|
927
|
+
`Verification script: ${name}`,
|
|
928
|
+
"observed",
|
|
929
|
+
"A root verification script is configured; its exact command remains in package.json.",
|
|
930
|
+
[{ source: "package.json", kind: "package_script" }]
|
|
931
|
+
)
|
|
932
|
+
);
|
|
933
|
+
const testPatternGroups = [
|
|
934
|
+
["*.test.*", /\.test\.[^/]+$/i],
|
|
935
|
+
["*.spec.*", /\.spec\.[^/]+$/i],
|
|
936
|
+
["__tests__/", /(^|\/)__tests__\//i],
|
|
937
|
+
["test_*.py", /(^|\/)test_[^/]+\.py$/i]
|
|
938
|
+
];
|
|
939
|
+
for (const [label, matcher] of testPatternGroups) {
|
|
940
|
+
const matches = testFiles.filter((source) => matcher.test(source));
|
|
941
|
+
if (matches.length === 0) {
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
verificationPatterns.push(
|
|
945
|
+
createBootstrapItem(
|
|
946
|
+
"verificationPatterns",
|
|
947
|
+
`Test pattern: ${label}`,
|
|
948
|
+
"observed",
|
|
949
|
+
`${matches.length} matching file(s) found in the bounded scan.`,
|
|
950
|
+
matches.slice(0, 6).map((source) => ({ source, kind: "test_file" }))
|
|
951
|
+
)
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
const sections = {
|
|
955
|
+
architecture: {
|
|
956
|
+
title: "Architecture",
|
|
957
|
+
status: architecture.length > 0 ? "observed" : "deferred",
|
|
958
|
+
detail: "Manifest, filesystem, dependency, and configuration patterns only.",
|
|
959
|
+
items: architecture.slice(0, 20)
|
|
960
|
+
},
|
|
961
|
+
conventions: {
|
|
962
|
+
title: "Conventions",
|
|
963
|
+
status: conventions.length > 0 ? "needs_review" : "deferred",
|
|
964
|
+
detail: "Guidance-language candidates require human review before durable import.",
|
|
965
|
+
items: conventions
|
|
966
|
+
},
|
|
967
|
+
decisions: {
|
|
968
|
+
title: "Decisions",
|
|
969
|
+
status: decisions.length > 0 ? "needs_review" : "deferred",
|
|
970
|
+
detail: "Commit and decision-document candidates are never auto-approved.",
|
|
971
|
+
items: decisions
|
|
972
|
+
},
|
|
973
|
+
procedures: {
|
|
974
|
+
title: "Procedures",
|
|
975
|
+
status: procedures.length > 0 ? "observed" : "deferred",
|
|
976
|
+
detail: "Configured scripts and CI files; execution order is not inferred.",
|
|
977
|
+
items: procedures.slice(0, 24)
|
|
978
|
+
},
|
|
979
|
+
skills: {
|
|
980
|
+
title: "Skills",
|
|
981
|
+
status: skills.some((item) => item.status === "needs_review") ? "needs_review" : skills.length > 0 ? "observed" : "deferred",
|
|
982
|
+
detail: "Explicit skill definitions plus reviewable workflow candidates.",
|
|
983
|
+
items: skills
|
|
984
|
+
},
|
|
985
|
+
recurringFailures: {
|
|
986
|
+
title: "Recurring failures",
|
|
987
|
+
status: recurringFailures.length > 0 ? "needs_review" : "deferred",
|
|
988
|
+
detail: "Only repeated failure-language signals are shown; no root cause is inferred.",
|
|
989
|
+
items: recurringFailures
|
|
990
|
+
},
|
|
991
|
+
criticalCode: {
|
|
992
|
+
title: "Critical code",
|
|
993
|
+
status: "deferred",
|
|
994
|
+
detail: "Critical symbols require Code Graph indexing; the local bootstrap does not guess them.",
|
|
995
|
+
items: []
|
|
996
|
+
},
|
|
997
|
+
verificationPatterns: {
|
|
998
|
+
title: "Verification patterns",
|
|
999
|
+
status: verificationPatterns.length > 0 ? "observed" : "deferred",
|
|
1000
|
+
detail: "Configured checks and test naming patterns from the bounded local scan.",
|
|
1001
|
+
items: verificationPatterns.slice(0, 24)
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
const summary = Object.fromEntries(
|
|
1005
|
+
Object.entries(sections).map(([key, section]) => [key, section.items.length])
|
|
1006
|
+
);
|
|
1007
|
+
const coverage = {
|
|
1008
|
+
gitHistory: createBootstrapCoverage(
|
|
1009
|
+
scan.git.detected ? "partial" : "unavailable",
|
|
1010
|
+
scan.git.detected ? "Inspected up to 120 recent commit subjects; commit bodies and diffs were not interpreted." : "No local Git repository was detected.",
|
|
1011
|
+
scan.git.detected ? ["git log"] : []
|
|
1012
|
+
),
|
|
1013
|
+
pullRequests: createBootstrapCoverage(
|
|
1014
|
+
"unavailable",
|
|
1015
|
+
"Pull request history requires an explicitly connected provider.",
|
|
1016
|
+
[]
|
|
1017
|
+
),
|
|
1018
|
+
issues: createBootstrapCoverage(
|
|
1019
|
+
"unavailable",
|
|
1020
|
+
"Issue history requires an explicitly connected provider.",
|
|
1021
|
+
[]
|
|
1022
|
+
),
|
|
1023
|
+
instructions: createBootstrapCoverage(
|
|
1024
|
+
instructionSources.length > 0 ? "observed" : "unavailable",
|
|
1025
|
+
instructionSources.length > 0 ? "Project instruction files were included in the bounded local scan." : "No AGENTS.md, CLAUDE.md, or Copilot instruction file was found.",
|
|
1026
|
+
instructionSources
|
|
1027
|
+
),
|
|
1028
|
+
documentation: createBootstrapCoverage(
|
|
1029
|
+
documentationSources.length > 0 ? "partial" : "unavailable",
|
|
1030
|
+
documentationSources.length > 0 ? `Inspected a bounded set of ${documentationSources.length} README, changelog, memory, and docs files.` : "No README, changelog, memory, or docs files were found.",
|
|
1031
|
+
documentationSources
|
|
1032
|
+
),
|
|
1033
|
+
ci: createBootstrapCoverage(
|
|
1034
|
+
ciFiles.length > 0 ? "observed" : "unavailable",
|
|
1035
|
+
ciFiles.length > 0 ? "CI configuration files were detected." : "No standard CI configuration was found.",
|
|
1036
|
+
ciFiles
|
|
1037
|
+
),
|
|
1038
|
+
packageScripts: createBootstrapCoverage(
|
|
1039
|
+
Object.keys(packageMetadata.scripts).length > 0 ? "observed" : "unavailable",
|
|
1040
|
+
Object.keys(packageMetadata.scripts).length > 0 ? "Root package scripts were inspected." : "No root package scripts were available.",
|
|
1041
|
+
Object.keys(packageMetadata.scripts).length > 0 ? ["package.json"] : []
|
|
1042
|
+
),
|
|
1043
|
+
testPatterns: createBootstrapCoverage(
|
|
1044
|
+
testFiles.length > 0 ? "partial" : "unavailable",
|
|
1045
|
+
testFiles.length > 0 ? `Found ${testFiles.length} test-like files within a ${PROJECT_BOOTSTRAP_MAX_WALKED_FILES}-file scan cap.` : `No test-like files were found within the ${PROJECT_BOOTSTRAP_MAX_WALKED_FILES}-file scan cap.`,
|
|
1046
|
+
testFiles.slice(0, 20)
|
|
1047
|
+
),
|
|
1048
|
+
releaseHistory: createBootstrapCoverage(
|
|
1049
|
+
releaseTags.length > 0 || exists("CHANGELOG.md") ? "partial" : "unavailable",
|
|
1050
|
+
releaseTags.length > 0 || exists("CHANGELOG.md") ? `Detected ${releaseTags.length} recent tag(s)${exists("CHANGELOG.md") ? " and CHANGELOG.md" : ""}.` : "No Git tags or root CHANGELOG.md were detected.",
|
|
1051
|
+
[
|
|
1052
|
+
...releaseTags.map((tag) => `git tag ${tag}`),
|
|
1053
|
+
...exists("CHANGELOG.md") ? ["CHANGELOG.md"] : []
|
|
1054
|
+
]
|
|
1055
|
+
),
|
|
1056
|
+
codeGraph: createBootstrapCoverage(
|
|
1057
|
+
"deferred",
|
|
1058
|
+
"Critical symbols and structural impact require Code Graph indexing after activation.",
|
|
1059
|
+
[]
|
|
1060
|
+
)
|
|
1061
|
+
};
|
|
1062
|
+
return {
|
|
1063
|
+
schemaVersion: PROJECT_BOOTSTRAP_SCHEMA_VERSION,
|
|
1064
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1065
|
+
repoPath: cwd,
|
|
1066
|
+
reviewPrompt: "Review what Snipara learned about your project.",
|
|
1067
|
+
scanLimits: {
|
|
1068
|
+
walkedFiles: PROJECT_BOOTSTRAP_MAX_WALKED_FILES,
|
|
1069
|
+
matchedFilesPerQuery: PROJECT_BOOTSTRAP_MAX_MATCHED_FILES,
|
|
1070
|
+
gitCommitSubjects: 120
|
|
1071
|
+
},
|
|
1072
|
+
summary,
|
|
1073
|
+
coverage,
|
|
1074
|
+
sections
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
function createBootstrapCoverage(status, detail, sources) {
|
|
1078
|
+
return { status, detail, sources };
|
|
1079
|
+
}
|
|
1080
|
+
function writeProjectBootstrap(report) {
|
|
1081
|
+
fs.mkdirSync(getActivationDir(), { recursive: true });
|
|
1082
|
+
fs.writeFileSync(getProjectBootstrapJsonPath(), JSON.stringify(report, null, 2) + "\n", "utf8");
|
|
1083
|
+
const lines = [
|
|
1084
|
+
"# PROJECT BOOTSTRAP",
|
|
1085
|
+
"",
|
|
1086
|
+
`Generated: ${report.generatedAt}`,
|
|
1087
|
+
`Repository: ${report.repoPath}`,
|
|
1088
|
+
"",
|
|
1089
|
+
"## Coverage",
|
|
1090
|
+
"",
|
|
1091
|
+
...Object.entries(report.coverage).map(
|
|
1092
|
+
([name, entry]) => `- ${name}: ${entry.status} \u2014 ${entry.detail}`
|
|
1093
|
+
),
|
|
1094
|
+
"",
|
|
1095
|
+
...Object.values(report.sections).flatMap((section) => [
|
|
1096
|
+
`## ${section.title} \u2014 ${section.items.length}`,
|
|
1097
|
+
"",
|
|
1098
|
+
section.detail,
|
|
1099
|
+
"",
|
|
1100
|
+
...section.items.length > 0 ? section.items.flatMap((item) => [
|
|
1101
|
+
`- [${item.status}] ${item.title}`,
|
|
1102
|
+
` ${item.detail}`,
|
|
1103
|
+
...item.evidence.length > 0 ? [
|
|
1104
|
+
` Evidence: ${item.evidence.map((entry) => entry.source).join(", ")}`
|
|
1105
|
+
] : []
|
|
1106
|
+
]) : ["- No evidence-backed item available yet."],
|
|
1107
|
+
""
|
|
1108
|
+
]),
|
|
1109
|
+
`**${report.reviewPrompt}**`,
|
|
1110
|
+
""
|
|
1111
|
+
];
|
|
1112
|
+
fs.writeFileSync(getProjectBootstrapMarkdownPath(), lines.join("\n"), "utf8");
|
|
1113
|
+
}
|
|
1114
|
+
function addProjectBootstrapToFirstWorkBrief(manifest, report) {
|
|
1115
|
+
manifest.firstBrief.sections.splice(1, 0, {
|
|
1116
|
+
title: "Project Bootstrap",
|
|
1117
|
+
lines: [
|
|
1118
|
+
`Architecture: ${report.summary.architecture} pattern(s).`,
|
|
1119
|
+
`Conventions: ${report.summary.conventions} candidate(s).`,
|
|
1120
|
+
`Decisions: ${report.summary.decisions} inferred candidate(s).`,
|
|
1121
|
+
`Procedures: ${report.summary.procedures} observed workflow(s).`,
|
|
1122
|
+
`Skills: ${report.summary.skills} existing or candidate workflow(s).`,
|
|
1123
|
+
`Recurring failures: ${report.summary.recurringFailures} review candidate(s).`,
|
|
1124
|
+
`Critical code: ${report.summary.criticalCode}; deferred until Code Graph indexes symbols.`,
|
|
1125
|
+
`Verification patterns: ${report.summary.verificationPatterns} observed pattern(s).`,
|
|
1126
|
+
report.reviewPrompt
|
|
1127
|
+
]
|
|
1128
|
+
});
|
|
582
1129
|
}
|
|
583
1130
|
function createInitialFirstWorkBrief(answers, detection, scan) {
|
|
584
1131
|
const surfaceContract = buildActivationSurfaceContract(answers.aiClient);
|
|
@@ -706,6 +1253,10 @@ function createActivationManifest(answers, detection, mode = "default", scan) {
|
|
|
706
1253
|
hasCompanion ? "queued" : "skipped",
|
|
707
1254
|
hasCompanion ? "Free Code Graph indexing starts from local source overlay within capacity caps." : "Free Code Graph is available after companion or hosted provider sync."
|
|
708
1255
|
),
|
|
1256
|
+
projectBootstrap: createActivationLane(
|
|
1257
|
+
"queued",
|
|
1258
|
+
"Bounded local cold-start analysis will prepare an evidence-linked Project Bootstrap."
|
|
1259
|
+
),
|
|
709
1260
|
memoryCandidates: createActivationLane(
|
|
710
1261
|
hasCompanion ? "needs_review" : "skipped",
|
|
711
1262
|
hasCompanion ? "Candidate decisions stay pending review; no durable memory is auto-approved." : "Install companion later to extract reviewable memory candidates."
|
|
@@ -728,7 +1279,9 @@ function createActivationManifest(answers, detection, mode = "default", scan) {
|
|
|
728
1279
|
legacyManifestPath: path.relative(process.cwd(), getActivationManifestPath()),
|
|
729
1280
|
firstBriefPath: null,
|
|
730
1281
|
handoffPath: null,
|
|
731
|
-
memoryCandidatesPath: null
|
|
1282
|
+
memoryCandidatesPath: null,
|
|
1283
|
+
projectBootstrapJsonPath: null,
|
|
1284
|
+
projectBootstrapMarkdownPath: null
|
|
732
1285
|
},
|
|
733
1286
|
nextActions: buildActivationNextActions(surfaceContract, answers),
|
|
734
1287
|
firstBrief: createInitialFirstWorkBrief(answers, detection, localScan),
|
|
@@ -918,6 +1471,22 @@ async function runHostedDayZeroCorpusSync(answers, manifest, enabled) {
|
|
|
918
1471
|
updateActivationManifest(manifest);
|
|
919
1472
|
}
|
|
920
1473
|
function createActivationArtifacts(answers, manifest) {
|
|
1474
|
+
const projectBootstrap = buildProjectBootstrap(manifest.localScan);
|
|
1475
|
+
writeProjectBootstrap(projectBootstrap);
|
|
1476
|
+
manifest.artifacts.projectBootstrapJsonPath = path.relative(
|
|
1477
|
+
process.cwd(),
|
|
1478
|
+
getProjectBootstrapJsonPath()
|
|
1479
|
+
);
|
|
1480
|
+
manifest.artifacts.projectBootstrapMarkdownPath = path.relative(
|
|
1481
|
+
process.cwd(),
|
|
1482
|
+
getProjectBootstrapMarkdownPath()
|
|
1483
|
+
);
|
|
1484
|
+
manifest.lanes.projectBootstrap = createActivationLane(
|
|
1485
|
+
"needs_review",
|
|
1486
|
+
"Evidence-linked Project Bootstrap generated; inferred candidates require review.",
|
|
1487
|
+
manifest.artifacts.projectBootstrapMarkdownPath
|
|
1488
|
+
);
|
|
1489
|
+
addProjectBootstrapToFirstWorkBrief(manifest, projectBootstrap);
|
|
921
1490
|
const candidates = buildMemoryCandidatePreviews(manifest.localScan);
|
|
922
1491
|
writeMemoryCandidates(candidates);
|
|
923
1492
|
manifest.artifacts.memoryCandidatesPath = path.relative(process.cwd(), getMemoryCandidatesPath());
|
|
@@ -3530,7 +4099,9 @@ function buildEditorActivationConsumerContract(client) {
|
|
|
3530
4099
|
activationManifest: ".snipara/activation/activation-manifest.json",
|
|
3531
4100
|
firstBrief: ".snipara/activation/first-brief.md",
|
|
3532
4101
|
handoff: ".snipara/activation/handoff.md",
|
|
3533
|
-
memoryCandidates: ".snipara/activation/memory-candidates.json"
|
|
4102
|
+
memoryCandidates: ".snipara/activation/memory-candidates.json",
|
|
4103
|
+
projectBootstrapJson: ".snipara/activation/project-bootstrap.json",
|
|
4104
|
+
projectBootstrapMarkdown: ".snipara/activation/project-bootstrap.md"
|
|
3534
4105
|
},
|
|
3535
4106
|
requiredManifestFields: [
|
|
3536
4107
|
"schemaVersion",
|
|
@@ -3539,7 +4110,10 @@ function buildEditorActivationConsumerContract(client) {
|
|
|
3539
4110
|
"artifacts.activationManifestPath",
|
|
3540
4111
|
"artifacts.firstBriefPath",
|
|
3541
4112
|
"artifacts.handoffPath",
|
|
4113
|
+
"artifacts.projectBootstrapJsonPath",
|
|
4114
|
+
"artifacts.projectBootstrapMarkdownPath",
|
|
3542
4115
|
"lanes.connection.status",
|
|
4116
|
+
"lanes.projectBootstrap.status",
|
|
3543
4117
|
"lanes.firstBrief.status",
|
|
3544
4118
|
"nextActions"
|
|
3545
4119
|
],
|
|
@@ -4715,51 +5289,6 @@ function describeJsonRpcError(data) {
|
|
|
4715
5289
|
const code = data.error.code !== void 0 ? ` ${data.error.code}` : "";
|
|
4716
5290
|
return `JSON-RPC error${code}: ${data.error.message || "unknown error"}`;
|
|
4717
5291
|
}
|
|
4718
|
-
function getCheck(report, name) {
|
|
4719
|
-
return report.checks.find((check) => check.name === name);
|
|
4720
|
-
}
|
|
4721
|
-
function formatCheckStatus(check) {
|
|
4722
|
-
if (!check) {
|
|
4723
|
-
return "not checked";
|
|
4724
|
-
}
|
|
4725
|
-
if (check.status === "pass") {
|
|
4726
|
-
return `OK - ${check.detail}`;
|
|
4727
|
-
}
|
|
4728
|
-
if (check.status === "fail") {
|
|
4729
|
-
return `FAILED - ${check.detail}`;
|
|
4730
|
-
}
|
|
4731
|
-
return `NEEDS ATTENTION - ${check.detail}`;
|
|
4732
|
-
}
|
|
4733
|
-
function printSetupVerdict(answers, report) {
|
|
4734
|
-
const hostedConnection = getCheck(report, "hosted_connection");
|
|
4735
|
-
const sniparaTools = getCheck(report, "hosted_snipara_tools");
|
|
4736
|
-
const legacyTools = getCheck(report, "hosted_legacy_tools_hidden");
|
|
4737
|
-
const codexConfig = getCheck(report, "codex_config");
|
|
4738
|
-
const mistralConfig = getCheck(report, "mistral_config");
|
|
4739
|
-
const claudeTools = getCheck(report, "claude_tools_visible");
|
|
4740
|
-
console.log(chalk.bold("\nSetup verdict:\n"));
|
|
4741
|
-
console.log(` Config written: ${answers.profile !== "runtime-only" ? "OK" : "not requested"}`);
|
|
4742
|
-
console.log(` Hosted MCP: ${formatCheckStatus(hostedConnection)}`);
|
|
4743
|
-
if (sniparaTools) {
|
|
4744
|
-
console.log(` Snipara tools: ${formatCheckStatus(sniparaTools)}`);
|
|
4745
|
-
}
|
|
4746
|
-
if (legacyTools) {
|
|
4747
|
-
console.log(` Legacy tool duplicates: ${formatCheckStatus(legacyTools)}`);
|
|
4748
|
-
}
|
|
4749
|
-
if (codexConfig) {
|
|
4750
|
-
console.log(` Codex config: ${formatCheckStatus(codexConfig)}`);
|
|
4751
|
-
}
|
|
4752
|
-
if (mistralConfig) {
|
|
4753
|
-
console.log(` Mistral config: ${formatCheckStatus(mistralConfig)}`);
|
|
4754
|
-
}
|
|
4755
|
-
if (answers.aiClient === "claude-code" || answers.aiClient === "claude-desktop") {
|
|
4756
|
-
console.log(
|
|
4757
|
-
` Claude loaded MCP: ${claudeTools ? formatCheckStatus(claudeTools) : "not verified"}`
|
|
4758
|
-
);
|
|
4759
|
-
} else {
|
|
4760
|
-
console.log(" AI client loaded MCP: not verifiable from this terminal");
|
|
4761
|
-
}
|
|
4762
|
-
}
|
|
4763
5292
|
function formatActivationStatus(status) {
|
|
4764
5293
|
if (status === "ready") {
|
|
4765
5294
|
return chalk.green("\u2713 ready");
|
|
@@ -4776,60 +5305,14 @@ function formatActivationStatus(status) {
|
|
|
4776
5305
|
return chalk.dim("skipped");
|
|
4777
5306
|
}
|
|
4778
5307
|
function printActivationSummary(manifest) {
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
["Connected", manifest.lanes.connection],
|
|
4782
|
-
["AI client configured", manifest.lanes.clientConfig],
|
|
4783
|
-
["Repository analyzed", manifest.lanes.localSnapshot],
|
|
4784
|
-
["Context index", manifest.lanes.contextIndex],
|
|
4785
|
-
["Code Graph", manifest.lanes.codeGraph],
|
|
4786
|
-
["Memory candidates", manifest.lanes.memoryCandidates],
|
|
4787
|
-
["Initial handoff", manifest.lanes.handoff],
|
|
4788
|
-
["First Work Brief", manifest.lanes.firstBrief],
|
|
4789
|
-
["GitHub sync", manifest.lanes.githubSync]
|
|
4790
|
-
];
|
|
4791
|
-
for (const [label, lane] of orderedLanes) {
|
|
4792
|
-
console.log(` ${formatActivationStatus(lane.status)} ${label} - ${lane.detail}`);
|
|
4793
|
-
}
|
|
4794
|
-
console.log(chalk.bold("\nFirst Work Brief:\n"));
|
|
4795
|
-
for (const section of manifest.firstBrief.sections) {
|
|
4796
|
-
console.log(` ${section.title}`);
|
|
4797
|
-
for (const line of section.lines.slice(0, 2)) {
|
|
4798
|
-
console.log(chalk.dim(` - ${line}`));
|
|
4799
|
-
}
|
|
4800
|
-
}
|
|
4801
|
-
console.log(chalk.bold("\nDay-zero proof:\n"));
|
|
4802
|
-
console.log(` Tool: ${manifest.dayZeroQuery.tool}`);
|
|
4803
|
-
console.log(` Query: ${manifest.dayZeroQuery.query}`);
|
|
5308
|
+
const contextLane = manifest.dayZeroCorpus.status === "failed" ? { status: "failed", detail: manifest.dayZeroCorpus.detail } : manifest.dayZeroCorpus.status === "submitted" || manifest.dayZeroCorpus.status === "queued" ? { status: "indexing", detail: manifest.dayZeroCorpus.detail } : manifest.lanes.contextIndex;
|
|
5309
|
+
console.log(chalk.bold("\nActivation status:\n"));
|
|
4804
5310
|
console.log(
|
|
4805
|
-
|
|
4806
|
-
` Target: ${manifest.activationMetric.targetMinutes} minutes to a true, non-obvious own-repo answer.`
|
|
4807
|
-
)
|
|
4808
|
-
);
|
|
4809
|
-
console.log(
|
|
4810
|
-
chalk.dim(
|
|
4811
|
-
` Day-zero corpus: ${manifest.dayZeroCorpus.status} - ${manifest.dayZeroCorpus.detail}`
|
|
4812
|
-
)
|
|
4813
|
-
);
|
|
4814
|
-
console.log(chalk.bold("\nActivation surface:\n"));
|
|
4815
|
-
console.log(` Engine: ${manifest.surfaceContract.engine}`);
|
|
4816
|
-
console.log(` Entry point: ${manifest.surfaceContract.entryPoint}`);
|
|
4817
|
-
console.log(chalk.dim(` Role: ${manifest.surfaceContract.clientRole}`));
|
|
4818
|
-
console.log(chalk.dim(` Next: ${manifest.surfaceContract.nextSurfaceAction}`));
|
|
4819
|
-
console.log(chalk.bold("\nAgent readiness:\n"));
|
|
4820
|
-
console.log(` ${manifest.agentReadiness.summary}`);
|
|
4821
|
-
for (const finding of manifest.agentReadiness.stalenessFindings.slice(0, 3)) {
|
|
4822
|
-
console.log(chalk.dim(` - ${finding.source}: ${finding.summary}`));
|
|
4823
|
-
}
|
|
4824
|
-
console.log(chalk.bold("\nContext economy:\n"));
|
|
4825
|
-
console.log(
|
|
4826
|
-
` Estimated ${manifest.contextEconomy.estimatedRetrievedTokens.toLocaleString("en-US")} tokens retrieved instead of ${manifest.contextEconomy.rawWindowBaselineTokens.toLocaleString("en-US")} raw-window tokens.`
|
|
4827
|
-
);
|
|
4828
|
-
console.log(
|
|
4829
|
-
chalk.dim(
|
|
4830
|
-
` Saved about ${manifest.contextEconomy.estimatedSavedTokens.toLocaleString("en-US")} tokens (${manifest.contextEconomy.savingsPercent}%). ${manifest.contextEconomy.note}`
|
|
4831
|
-
)
|
|
5311
|
+
` ${formatActivationStatus(manifest.lanes.connection.status)} Connected - ${manifest.lanes.connection.detail}`
|
|
4832
5312
|
);
|
|
5313
|
+
console.log(` ${formatActivationStatus(contextLane.status)} Context - ${contextLane.detail}`);
|
|
5314
|
+
console.log(` ${chalk.dim("\u25CB pending")} First grounded answer`);
|
|
5315
|
+
console.log(chalk.yellow("\n Activation pending: first grounded answer needed."));
|
|
4833
5316
|
console.log(
|
|
4834
5317
|
chalk.dim(
|
|
4835
5318
|
`
|
|
@@ -4840,12 +5323,7 @@ function printActivationSummary(manifest) {
|
|
|
4840
5323
|
console.log(chalk.dim(` Initial handoff: ${manifest.artifacts.handoffPath}`));
|
|
4841
5324
|
}
|
|
4842
5325
|
if (manifest.artifacts.memoryCandidatesPath) {
|
|
4843
|
-
console.log(chalk.dim(`
|
|
4844
|
-
}
|
|
4845
|
-
if (manifest.mode === "starter") {
|
|
4846
|
-
console.log(
|
|
4847
|
-
chalk.dim(` Starter tour: ${BASE_URL}/demo#sixty-second-demo and ${BASE_URL}/dashboard`)
|
|
4848
|
-
);
|
|
5326
|
+
console.log(chalk.dim(` Advanced details: ${manifest.artifacts.memoryCandidatesPath}`));
|
|
4849
5327
|
}
|
|
4850
5328
|
}
|
|
4851
5329
|
function printSuccess(answers, report, activationManifest) {
|
|
@@ -4853,160 +5331,28 @@ function printSuccess(answers, report, activationManifest) {
|
|
|
4853
5331
|
chalk.green(`
|
|
4854
5332
|
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
4855
5333
|
\u2551 \u2551
|
|
4856
|
-
\u2551 ${chalk.bold("\u2713
|
|
5334
|
+
\u2551 ${chalk.bold("\u2713 Connection Ready")} \u2551
|
|
4857
5335
|
\u2551 \u2551
|
|
4858
5336
|
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
4859
5337
|
`)
|
|
4860
5338
|
);
|
|
4861
|
-
console.log(
|
|
4862
|
-
console.log(` ${chalk.green("\u2713")} ${answers.profile}`);
|
|
4863
|
-
printSetupVerdict(answers, report);
|
|
4864
|
-
console.log(chalk.bold("\nWhat was installed:\n"));
|
|
4865
|
-
if (answers.installMcp) {
|
|
4866
|
-
console.log(
|
|
4867
|
-
chalk.green(" \u2713 snipara-mcp") + chalk.dim(" - Local stdio compatibility package")
|
|
4868
|
-
);
|
|
4869
|
-
}
|
|
4870
|
-
if (answers.installRuntime) {
|
|
4871
|
-
console.log(
|
|
4872
|
-
chalk.green(` \u2713 ${stripExtras(SANDBOX_PACKAGE_NAME)}`) + chalk.dim(" - Safe code execution")
|
|
4873
|
-
);
|
|
4874
|
-
}
|
|
4875
|
-
if (answers.installOrchestrator) {
|
|
4876
|
-
console.log(
|
|
4877
|
-
chalk.green(` \u2713 ${ORCHESTRATOR_PACKAGE_NAME}`) + chalk.dim(" - Production gates, drift checks, and htask workflows")
|
|
4878
|
-
);
|
|
4879
|
-
}
|
|
4880
|
-
if (answers.installCompanion) {
|
|
4881
|
-
console.log(
|
|
4882
|
-
chalk.green(` \u2713 ${COMPANION_PACKAGE_NAME}`) + chalk.dim(" - Local companion workflows and hook bootstrap")
|
|
4883
|
-
);
|
|
4884
|
-
}
|
|
4885
|
-
if (answers.installHooks) {
|
|
4886
|
-
console.log(chalk.green(" \u2713 hooks") + chalk.dim(" - Local session lifecycle scripts"));
|
|
4887
|
-
}
|
|
4888
|
-
if (answers.profile !== "runtime-only") {
|
|
4889
|
-
console.log(chalk.green(" \u2713 .mcp.json") + chalk.dim(" - Hosted MCP config"));
|
|
4890
|
-
}
|
|
4891
|
-
if (answers.aiClient === "claude-code" && answers.profile !== "runtime-only") {
|
|
4892
|
-
console.log(
|
|
4893
|
-
chalk.green(" \u2713 Claude Code") + chalk.dim(" - .mcp.json MCP config, .claude/settings.json hooks when enabled")
|
|
4894
|
-
);
|
|
4895
|
-
}
|
|
4896
|
-
if (answers.aiClient === "kimi" && answers.profile !== "runtime-only") {
|
|
4897
|
-
console.log(
|
|
4898
|
-
chalk.green(" \u2713 Kimi Code CLI") + chalk.dim(" - .kimi-code/mcp.json with environment-backed authentication")
|
|
4899
|
-
);
|
|
4900
|
-
}
|
|
4901
|
-
console.log(
|
|
4902
|
-
chalk.green(" \u2713 .snipara/companion") + chalk.dim(" - Doctor report and optional command references")
|
|
4903
|
-
);
|
|
5339
|
+
console.log(` Profile: ${answers.profile}`);
|
|
4904
5340
|
if (activationManifest) {
|
|
4905
5341
|
printActivationSummary(activationManifest);
|
|
4906
5342
|
}
|
|
4907
5343
|
const failedChecks = report.checks.filter((check) => check.status === "fail");
|
|
4908
5344
|
const warnedChecks = report.checks.filter((check) => check.status === "warn");
|
|
4909
|
-
console.log(chalk.bold("\nNext
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
console.log(
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
if (
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
);
|
|
4920
|
-
}
|
|
4921
|
-
if (answers.aiClient === "claude-code") {
|
|
4922
|
-
printNextStep(
|
|
4923
|
-
"For Claude Code, start a new session and confirm Snipara appears in Settings -> MCP or ToolSearch."
|
|
4924
|
-
);
|
|
4925
|
-
printNextStep(
|
|
4926
|
-
`If Snipara tools are missing, run: ${CREATE_SNIPARA_NPX_COMMAND} doctor --client claude-code`
|
|
4927
|
-
);
|
|
4928
|
-
}
|
|
4929
|
-
if (answers.aiClient === "claude-desktop") {
|
|
4930
|
-
printNextStep(
|
|
4931
|
-
"For Claude Desktop, confirm the Snipara server appears in the desktop MCP settings after restart."
|
|
4932
|
-
);
|
|
4933
|
-
printNextStep(
|
|
4934
|
-
`If Snipara tools are missing, run: ${CREATE_SNIPARA_NPX_COMMAND} doctor --client claude-desktop`
|
|
4935
|
-
);
|
|
4936
|
-
}
|
|
4937
|
-
if (answers.aiClient === "kimi") {
|
|
4938
|
-
printNextStep(
|
|
4939
|
-
"For Kimi Code CLI, export SNIPARA_API_KEY before launch and run /mcp to verify the project-local server."
|
|
4940
|
-
);
|
|
4941
|
-
printNextStep(
|
|
4942
|
-
`To enable Companion lifecycle hooks, run ${COMPANION_NPX_COMMAND} automations install --client kimi, review .kimi-code/snipara-plugin, then install it in Kimi with /plugins install .kimi-code/snipara-plugin.`
|
|
4943
|
-
);
|
|
4944
|
-
printNextStep(
|
|
4945
|
-
"Keep manual approval for risky tools because Kimi hooks are fail-open and are not a standalone security boundary."
|
|
4946
|
-
);
|
|
4947
|
-
}
|
|
4948
|
-
if (answers.aiClient === "codex") {
|
|
4949
|
-
printNextStep(
|
|
4950
|
-
"For Codex, merge .snipara/templates/codex-config.toml into ~/.codex/config.toml if the app does not read project .codex/config.toml."
|
|
4951
|
-
);
|
|
4952
|
-
printNextStep(
|
|
4953
|
-
"Restart Codex after changing ~/.codex/config.toml so it rediscovers the hosted Snipara MCP tools."
|
|
4954
|
-
);
|
|
4955
|
-
printNextStep(
|
|
4956
|
-
"If Codex shows only the lean Snipara tool set, that is expected. Use snipara_help(query=...) for routed guidance, confirm specialist opt-in surfaces with snipara_help(list_all=true), and request exact htask or swarm tools only when the workflow explicitly needs them."
|
|
4957
|
-
);
|
|
4958
|
-
printNextStep(
|
|
4959
|
-
"If Codex still does not expose the hosted task-routing tools, prefer snipara-orchestrator swarm-* and htask-* for shared queue or multi-agent task work."
|
|
4960
|
-
);
|
|
4961
|
-
}
|
|
4962
|
-
if (answers.aiClient === "mistral") {
|
|
4963
|
-
printNextStep(
|
|
4964
|
-
"For Mistral Le Chat, add .snipara/templates/mistral-le-chat-mcp.json as a Custom MCP Connector reference in Connectors."
|
|
4965
|
-
);
|
|
4966
|
-
printNextStep(
|
|
4967
|
-
"For Mistral Vibe, keep .vibe/config.toml or ~/.vibe/config.toml on the generated SNIPARA_API_KEY-backed streamable-http server block, then restart Vibe."
|
|
4968
|
-
);
|
|
4969
|
-
printNextStep(
|
|
4970
|
-
"For LangChain JavaScript, adapt .snipara/templates/mistral-langchain-tools.ts to bind Snipara recall, context_query, and settings tools to ChatMistralAI."
|
|
4971
|
-
);
|
|
4972
|
-
}
|
|
4973
|
-
printNextStep(
|
|
4974
|
-
`Run ${CREATE_SNIPARA_NPX_COMMAND} doctor anytime you want a fresh local validation report.`
|
|
4975
|
-
);
|
|
4976
|
-
if (answers.installCompanion) {
|
|
4977
|
-
printNextStep("Open .snipara/companion/README.md for local commands and repair flow.");
|
|
4978
|
-
printNextStep(
|
|
4979
|
-
`${COMPANION_COMMAND_NAME} ${MIN_COMPANION_VERSION}+ is installed for low-latency local hooks.`
|
|
4980
|
-
);
|
|
4981
|
-
printNextStep(`Activate local source context now with: ${COMPANION_NPX_COMMAND} source init .`);
|
|
4982
|
-
printNextStep(
|
|
4983
|
-
`Use ${COMPANION_NPX_COMMAND} recall / query / plan / upload / task-commit for explicit local workflows and as a fallback when an AI client exposes only a partial MCP surface.`
|
|
4984
|
-
);
|
|
4985
|
-
printNextStep(
|
|
4986
|
-
`For business folders, preview with: ${COMPANION_NPX_COMMAND} onboard-folder ./client-export --source-provider local_folder --write-manifest ./snipara-onboard.json`
|
|
4987
|
-
);
|
|
4988
|
-
printNextStep(
|
|
4989
|
-
`Apply after review with: ${COMPANION_NPX_COMMAND} onboard-folder ./client-export --source-provider local_folder --apply`
|
|
4990
|
-
);
|
|
4991
|
-
} else if (answers.profile !== "runtime-only") {
|
|
4992
|
-
printNextStep(
|
|
4993
|
-
`Enable Companion, guards, managed workflows, and local folder helpers later with: ${CREATE_SNIPARA_NPX_COMMAND} repair --with-companion`
|
|
4994
|
-
);
|
|
4995
|
-
}
|
|
4996
|
-
if (answers.installRuntime && !answers.runSandboxInit) {
|
|
4997
|
-
printNextStep(
|
|
4998
|
-
`Run ${SANDBOX_COMMAND_NAME} init when you want to choose sandbox/docker/local execution.`
|
|
4999
|
-
);
|
|
5000
|
-
}
|
|
5001
|
-
if (answers.installOrchestrator) {
|
|
5002
|
-
printNextStep(
|
|
5003
|
-
`Use ${ORCHESTRATOR_COMMAND_NAME} explicitly for production proof gates, drift checks, or htask coordination.`
|
|
5004
|
-
);
|
|
5005
|
-
}
|
|
5006
|
-
if (answers.githubRepo) {
|
|
5007
|
-
printNextStep(
|
|
5008
|
-
`Connect GitHub automation for ${answers.githubRepo.fullName} from the generated Snipara link.`
|
|
5009
|
-
);
|
|
5345
|
+
console.log(chalk.bold("\nNext action:\n"));
|
|
5346
|
+
if (activationManifest) {
|
|
5347
|
+
console.log(" Restart your AI client or open a new agent session, then ask:");
|
|
5348
|
+
console.log(chalk.cyan(`
|
|
5349
|
+
"${activationManifest.dayZeroQuery.query}"
|
|
5350
|
+
`));
|
|
5351
|
+
console.log(chalk.dim(` If Snipara tools are missing: ${CREATE_SNIPARA_NPX_COMMAND} doctor`));
|
|
5352
|
+
} else if (answers.installRuntime && !answers.runSandboxInit) {
|
|
5353
|
+
console.log(` Run ${SANDBOX_COMMAND_NAME} init to choose an execution backend.`);
|
|
5354
|
+
} else {
|
|
5355
|
+
console.log(` Run ${CREATE_SNIPARA_NPX_COMMAND} doctor to verify the installation.`);
|
|
5010
5356
|
}
|
|
5011
5357
|
if (failedChecks.length > 0 || warnedChecks.length > 0) {
|
|
5012
5358
|
console.log(chalk.bold("\nValidation summary:\n"));
|
|
@@ -5014,11 +5360,9 @@ function printSuccess(answers, report, activationManifest) {
|
|
|
5014
5360
|
const symbol = check.status === "fail" ? chalk.red("\u2717") : chalk.yellow("!");
|
|
5015
5361
|
console.log(` ${symbol} ${check.name}: ${check.detail}`);
|
|
5016
5362
|
}
|
|
5017
|
-
console.log(
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
`)
|
|
5021
|
-
);
|
|
5363
|
+
console.log(chalk.dim(`
|
|
5364
|
+
Repair local wiring: ${CREATE_SNIPARA_NPX_COMMAND} repair
|
|
5365
|
+
`));
|
|
5022
5366
|
} else {
|
|
5023
5367
|
console.log(
|
|
5024
5368
|
chalk.dim(
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-snipara",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "Onboard Snipara Hosted MCP Context + Memory with
|
|
3
|
+
"version": "2.0.16",
|
|
4
|
+
"description": "Onboard Snipara Hosted MCP Context + Memory with project bootstrap intelligence and optional local workflows",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
12
12
|
"dev": "tsup src/index.ts --format esm --watch",
|
|
13
|
-
"
|
|
13
|
+
"prepack": "npm run build",
|
|
14
14
|
"pretest": "pnpm build",
|
|
15
15
|
"test": "node --test test/*.test.cjs",
|
|
16
16
|
"pack:smoke": "bash ../../scripts/smoke-node-package.sh packages/create-snipara create-snipara create-snipara --help",
|