@ignite-agent/agent 0.5.1 → 0.6.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/README.md +24 -0
- package/dist/cli/index.js +87 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/ignite.css +10 -0
- package/dist/operator/ui.js +10 -0
- package/dist/operator/ui.js.map +1 -1
- package/dist/wiki-content/index.d.ts +30 -0
- package/dist/wiki-content/index.js +68 -0
- package/dist/wiki-content/index.js.map +1 -0
- package/dist/wiki-content/jsonld.d.ts +20 -0
- package/dist/wiki-content/jsonld.js +88 -0
- package/dist/wiki-content/jsonld.js.map +1 -0
- package/dist/wiki-content/load.d.ts +22 -0
- package/dist/wiki-content/load.js +92 -0
- package/dist/wiki-content/load.js.map +1 -0
- package/dist/wiki-content/metadata.d.ts +13 -0
- package/dist/wiki-content/metadata.js +61 -0
- package/dist/wiki-content/metadata.js.map +1 -0
- package/dist/wiki-content/pull.d.ts +23 -0
- package/dist/wiki-content/pull.js +160 -0
- package/dist/wiki-content/pull.js.map +1 -0
- package/dist/wiki-content/render.d.ts +29 -0
- package/dist/wiki-content/render.js +80 -0
- package/dist/wiki-content/render.js.map +1 -0
- package/dist/wiki-content/schema.d.ts +73 -0
- package/dist/wiki-content/schema.js +173 -0
- package/dist/wiki-content/schema.js.map +1 -0
- package/package.json +8 -4
- package/skills/ignite-brand-wiki/SKILL.md +130 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const ENTITY_TYPES = ['company', 'product', 'person', 'concept', 'location', 'offering'];
|
|
3
|
+
const slug = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, 'slug must be kebab-case: a-z, 0-9 and single dashes');
|
|
4
|
+
const isoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'date must be YYYY-MM-DD');
|
|
5
|
+
const httpUrl = z.string().regex(/^https?:\/\/\S+$/, 'url must be absolute http(s)');
|
|
6
|
+
const text = z.string().trim().min(1);
|
|
7
|
+
const definition = z
|
|
8
|
+
.object({
|
|
9
|
+
lede: z.string().trim().optional(),
|
|
10
|
+
category: z.string().trim().optional(),
|
|
11
|
+
founded: z.string().trim().optional(),
|
|
12
|
+
function: z.string().trim().optional(),
|
|
13
|
+
differentiator: z.string().trim().optional(),
|
|
14
|
+
})
|
|
15
|
+
.superRefine((value, ctx) => {
|
|
16
|
+
const hasLede = (value.lede ?? '') !== '';
|
|
17
|
+
const hasFields = (value.category ?? '') !== '' && (value.function ?? '') !== '';
|
|
18
|
+
if (!hasLede && !hasFields) {
|
|
19
|
+
ctx.addIssue({
|
|
20
|
+
code: 'custom',
|
|
21
|
+
message: 'definition needs a non-empty lede, or both category and function',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
export const wikiEntitySchema = z.object({
|
|
26
|
+
entity_type: z.enum(ENTITY_TYPES),
|
|
27
|
+
canonical_name: text,
|
|
28
|
+
slug,
|
|
29
|
+
aliases: z.array(text).default([]),
|
|
30
|
+
definition,
|
|
31
|
+
sections: z
|
|
32
|
+
.array(z.object({
|
|
33
|
+
heading: text,
|
|
34
|
+
paragraphs: z.array(text).default([]),
|
|
35
|
+
stats: z
|
|
36
|
+
.array(z.object({
|
|
37
|
+
text,
|
|
38
|
+
citation: z.number().int().min(1),
|
|
39
|
+
}))
|
|
40
|
+
.default([]),
|
|
41
|
+
}))
|
|
42
|
+
.default([]),
|
|
43
|
+
cross_links: z.array(z.object({ target_slug: slug, anchor: z.string().optional() })).default([]),
|
|
44
|
+
citations: z
|
|
45
|
+
.array(z.object({
|
|
46
|
+
n: z.number().int().min(1),
|
|
47
|
+
url: httpUrl,
|
|
48
|
+
title: text,
|
|
49
|
+
accessed: isoDate,
|
|
50
|
+
}))
|
|
51
|
+
.default([]),
|
|
52
|
+
author: z.object({ name: text, url: httpUrl.optional() }).optional(),
|
|
53
|
+
changelog: z.array(z.object({ date: isoDate, note: text })).default([]),
|
|
54
|
+
same_as: z.array(httpUrl).default([]),
|
|
55
|
+
// Present on files `ignite wiki pull` wrote. A file without it was authored by a person and is never overwritten.
|
|
56
|
+
imported_from: z
|
|
57
|
+
.object({
|
|
58
|
+
source: z.literal('ignite'),
|
|
59
|
+
slug: z.string(),
|
|
60
|
+
dateModified: z.string(),
|
|
61
|
+
})
|
|
62
|
+
.optional(),
|
|
63
|
+
});
|
|
64
|
+
// Parses one file's JSON against the schema. Returns issues instead of throwing so a validator can list every file.
|
|
65
|
+
export function parseWikiEntity(file, raw) {
|
|
66
|
+
const result = wikiEntitySchema.safeParse(raw);
|
|
67
|
+
if (!result.success) {
|
|
68
|
+
return {
|
|
69
|
+
entity: null,
|
|
70
|
+
issues: result.error.issues.map((issue) => ({
|
|
71
|
+
file,
|
|
72
|
+
message: `${issue.path.length > 0 ? `${issue.path.join('.')}: ` : ''}${issue.message}`,
|
|
73
|
+
})),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return { entity: result.data, issues: [] };
|
|
77
|
+
}
|
|
78
|
+
// The cross-file invariants: everything a single-file schema cannot see.
|
|
79
|
+
export function validateWikiSet(entries) {
|
|
80
|
+
const issues = [];
|
|
81
|
+
const bySlug = new Map();
|
|
82
|
+
for (const { file, entity } of entries) {
|
|
83
|
+
const expected = fileSlug(file);
|
|
84
|
+
if (expected !== null && expected !== entity.slug) {
|
|
85
|
+
issues.push({ file, message: `slug "${entity.slug}" does not match the file name "${expected}"` });
|
|
86
|
+
}
|
|
87
|
+
const seenIn = bySlug.get(entity.slug);
|
|
88
|
+
if (seenIn) {
|
|
89
|
+
issues.push({ file, message: `duplicate slug "${entity.slug}" (also in ${seenIn})` });
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
bySlug.set(entity.slug, file);
|
|
93
|
+
}
|
|
94
|
+
const citationIndices = new Set();
|
|
95
|
+
for (const citation of entity.citations) {
|
|
96
|
+
if (citationIndices.has(citation.n)) {
|
|
97
|
+
issues.push({ file, message: `duplicate citation index [${citation.n}]` });
|
|
98
|
+
}
|
|
99
|
+
citationIndices.add(citation.n);
|
|
100
|
+
}
|
|
101
|
+
entity.sections.forEach((section, s) => {
|
|
102
|
+
section.stats.forEach((stat, i) => {
|
|
103
|
+
if (!citationIndices.has(stat.citation)) {
|
|
104
|
+
issues.push({
|
|
105
|
+
file,
|
|
106
|
+
message: `sections[${s}].stats[${i}] cites [${stat.citation}] but no citation has n=${stat.citation}`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
for (const { file, entity } of entries) {
|
|
113
|
+
for (const link of entity.cross_links) {
|
|
114
|
+
if (!bySlug.has(link.target_slug)) {
|
|
115
|
+
issues.push({ file, message: `cross_links target "${link.target_slug}" resolves to no entity file` });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return issues;
|
|
120
|
+
}
|
|
121
|
+
export function fileSlug(file) {
|
|
122
|
+
const base = file.split(/[\\/]/).pop() ?? '';
|
|
123
|
+
if (!base.endsWith('.json'))
|
|
124
|
+
return null;
|
|
125
|
+
const stem = base.slice(0, -'.json'.length);
|
|
126
|
+
return stem.startsWith('_') ? null : stem;
|
|
127
|
+
}
|
|
128
|
+
export function entityTypeLabel(type) {
|
|
129
|
+
const labels = {
|
|
130
|
+
company: 'Company',
|
|
131
|
+
product: 'Product',
|
|
132
|
+
person: 'Person',
|
|
133
|
+
concept: 'Concept',
|
|
134
|
+
location: 'Location',
|
|
135
|
+
offering: 'Service',
|
|
136
|
+
};
|
|
137
|
+
return labels[type];
|
|
138
|
+
}
|
|
139
|
+
export function schemaOrgType(type) {
|
|
140
|
+
const map = {
|
|
141
|
+
company: 'Organization',
|
|
142
|
+
product: 'Product',
|
|
143
|
+
person: 'Person',
|
|
144
|
+
concept: 'Thing',
|
|
145
|
+
location: 'Place',
|
|
146
|
+
offering: 'Service',
|
|
147
|
+
};
|
|
148
|
+
return map[type];
|
|
149
|
+
}
|
|
150
|
+
// The canonical definition block as one lede paragraph: the stored lede when there is one, otherwise the
|
|
151
|
+
// fields joined in the order the template prescribes. Nothing is invented for a missing field.
|
|
152
|
+
export function definitionLede(entity) {
|
|
153
|
+
const d = entity.definition;
|
|
154
|
+
if (d.lede && d.lede.trim() !== '')
|
|
155
|
+
return d.lede.trim();
|
|
156
|
+
const category = d.category?.trim() || entityTypeLabel(entity.entity_type).toLowerCase();
|
|
157
|
+
const parts = [`${entity.canonical_name} is a ${category}`];
|
|
158
|
+
if (d.founded?.trim())
|
|
159
|
+
parts.push(`founded in ${d.founded.trim()}`);
|
|
160
|
+
if (d.function?.trim())
|
|
161
|
+
parts.push(`that ${stripTrailingPeriod(d.function.trim())}`);
|
|
162
|
+
let lede = `${parts.join(' ')}.`;
|
|
163
|
+
if (d.differentiator?.trim())
|
|
164
|
+
lede += ` ${ensurePeriod(d.differentiator.trim())}`;
|
|
165
|
+
return lede;
|
|
166
|
+
}
|
|
167
|
+
function stripTrailingPeriod(value) {
|
|
168
|
+
return value.replace(/\.+$/, '');
|
|
169
|
+
}
|
|
170
|
+
function ensurePeriod(value) {
|
|
171
|
+
return /[.!?]$/.test(value) ? value : `${value}.`;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/wiki-content/schema.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ignite-agent/agent",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Ignite for Next.js: App Router SDK for robots, sitemap, JSON-LD, metadata, crawl logging, hosted wiki/blog pages,
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Ignite for Next.js: App Router SDK for robots, sitemap, JSON-LD, metadata, crawl logging, hosted wiki/blog pages, a content-inventory seed, and a validated Brand Wiki from repo JSON. Server-only. The front end reads JSON; MCP is for coding agents, not the browser.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
7
7
|
"private": false,
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"dist",
|
|
19
19
|
"onboarding",
|
|
20
|
+
"skills",
|
|
20
21
|
"LICENSE",
|
|
21
22
|
"AI.txt",
|
|
22
23
|
"dist/ignite.css",
|
|
@@ -34,16 +35,18 @@
|
|
|
34
35
|
"./wiki": "./dist/operator/wiki.js",
|
|
35
36
|
"./blog": "./dist/operator/blog.js",
|
|
36
37
|
"./client": "./dist/client-public.js",
|
|
38
|
+
"./wiki-content": "./dist/wiki-content/index.js",
|
|
37
39
|
"./styles.css": "./dist/ignite.css"
|
|
38
40
|
},
|
|
39
41
|
"scripts": {
|
|
40
42
|
"build": "tsc -p tsconfig.json && node scripts/write-css.mjs",
|
|
41
43
|
"prepublishOnly": "npm run build",
|
|
42
44
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
43
|
-
"test": "npm run build && node --test dist/client.test.js dist/mcp.test.js dist/operator/ui.test.js dist/operator/sitemap-merge.test.js dist/cli/project.test.js dist/cli/seed.test.js"
|
|
45
|
+
"test": "npm run build && node --test dist/client.test.js dist/mcp.test.js dist/operator/ui.test.js dist/operator/sitemap-merge.test.js dist/cli/project.test.js dist/cli/seed.test.js dist/wiki-content/wiki.test.js"
|
|
44
46
|
},
|
|
45
47
|
"dependencies": {
|
|
46
|
-
"server-only": "^0.0.1"
|
|
48
|
+
"server-only": "^0.0.1",
|
|
49
|
+
"zod": "^4.5.4"
|
|
47
50
|
},
|
|
48
51
|
"peerDependencies": {
|
|
49
52
|
"next": ">=15.0.0 <17.0.0",
|
|
@@ -57,6 +60,7 @@
|
|
|
57
60
|
"devDependencies": {
|
|
58
61
|
"@types/node": "^22.0.0",
|
|
59
62
|
"@types/react": "^19.0.0",
|
|
63
|
+
"@types/react-dom": "^19.2.7",
|
|
60
64
|
"next": "^16.3.0",
|
|
61
65
|
"react": "^19.0.0",
|
|
62
66
|
"typescript": "^5.6.0"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ignite-brand-wiki
|
|
3
|
+
description: Build and maintain the Brand Wiki in a Next.js App Router site with @ignite-agent/agent — entity files in content/wiki, six fixed page regions, a build-time honesty gate, factual JSON-LD. Use when asked to add, generate, pull, validate or edit the wiki, or when touching content/wiki/*.json or app/wiki/*.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Brand Wiki on Next.js
|
|
7
|
+
|
|
8
|
+
One entity per page. Content lives in the repo as validated JSON under `content/wiki/`. Git is the CMS: a
|
|
9
|
+
page changes through a reviewable diff, never through an admin UI. Pages render statically; nothing in the
|
|
10
|
+
content path runs in the browser.
|
|
11
|
+
|
|
12
|
+
The structure is evidence-based. It is not a ranking lever, and JSON-LD here is factual structure only.
|
|
13
|
+
Never describe it to the user as a way to get cited or ranked.
|
|
14
|
+
|
|
15
|
+
## Before you start
|
|
16
|
+
|
|
17
|
+
- App Router project (`app/` or `src/app/`), `@ignite-agent/agent` >= 0.6 installed.
|
|
18
|
+
- `IGNITE_SITE_ID`, `IGNITE_SITE_TOKEN` and `IGNITE_SITE_URL` set in `.env.local` and in the host. Run
|
|
19
|
+
`npx ignite doctor` if unsure. The token stays on the server. Never `NEXT_PUBLIC_`.
|
|
20
|
+
- Optional: `IGNITE_SITE_NAME` (title suffix), `IGNITE_WIKI_BASE_PATH` (default `/wiki`), `IGNITE_LOCALE`
|
|
21
|
+
(must match `<html lang>`).
|
|
22
|
+
|
|
23
|
+
## 1. Pull the pages Ignite already wrote
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx ignite wiki pull --dry-run # lists what would be written
|
|
27
|
+
npx ignite wiki pull
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Each published wiki page in the user's Ignite workspace becomes `content/wiki/<slug>.json`. The import
|
|
31
|
+
carries only what the page states: the lede, the sections, the paragraphs that cite a source, the source
|
|
32
|
+
list, the author, the dates. It starts every file as `entity_type: "concept"` with no aliases, and it
|
|
33
|
+
records `imported_from`. A file without `imported_from` was written by a person and is never overwritten.
|
|
34
|
+
|
|
35
|
+
## 2. Review every imported file with the user
|
|
36
|
+
|
|
37
|
+
For each file, settle with the user (do not guess):
|
|
38
|
+
|
|
39
|
+
- `entity_type`: `company | product | person | concept | location | offering`.
|
|
40
|
+
- `canonical_name` and `aliases`. Every anchor on the site uses the canonical form; an alias is never an anchor.
|
|
41
|
+
- `definition`: either a `lede` paragraph, or `category` + `function` (+ optional `founded`,
|
|
42
|
+
`differentiator`). The lede is the first crawlable paragraph. Leave a field out rather than invent it.
|
|
43
|
+
- `cross_links`: `target_slug` of related entities. The anchor text rendered is the target's canonical name.
|
|
44
|
+
- `same_as`: only real, public profile URLs the user confirms.
|
|
45
|
+
|
|
46
|
+
A stat without a real source is deleted, not sourced from memory. A citation needs `n`, an http(s) `url`, a
|
|
47
|
+
`title` and an `accessed` date (`YYYY-MM-DD`). Dates and founders are facts the user supplies, never
|
|
48
|
+
inferred.
|
|
49
|
+
|
|
50
|
+
## 3. Write new entities
|
|
51
|
+
|
|
52
|
+
Same shape, by hand or with the user. Minimum viable file:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"entity_type": "company",
|
|
57
|
+
"canonical_name": "Northwind",
|
|
58
|
+
"slug": "northwind",
|
|
59
|
+
"aliases": ["Northwind Traders"],
|
|
60
|
+
"definition": { "category": "logistics software", "founded": "2019", "function": "routes regional freight for small carriers", "differentiator": "Runs on the carrier's own data; nothing is pooled across customers." },
|
|
61
|
+
"sections": [
|
|
62
|
+
{ "heading": "What does Northwind do?", "paragraphs": ["…"], "stats": [{ "text": "Carriers on Northwind cut empty miles by 12% in 2025 [1].", "citation": 1 }] }
|
|
63
|
+
],
|
|
64
|
+
"cross_links": [{ "target_slug": "route-planning" }],
|
|
65
|
+
"citations": [{ "n": 1, "url": "https://northwind.example/reports/2025", "title": "Northwind 2025 operations report", "accessed": "2026-09-01" }],
|
|
66
|
+
"author": { "name": "Northwind editorial", "url": "https://northwind.example/about" },
|
|
67
|
+
"changelog": [{ "date": "2026-09-01", "note": "First published." }],
|
|
68
|
+
"same_as": ["https://www.linkedin.com/company/northwind"]
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Section headings are question-shaped. `[n]` markers in text link to `#cite-n`.
|
|
73
|
+
|
|
74
|
+
## 4. Wire the routes (greenfield only; never overwrite a host file)
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// app/wiki/[slug]/page.tsx
|
|
78
|
+
export { default, generateStaticParams, generateMetadata, dynamicParams, revalidate } from '@ignite-agent/agent/wiki-content'
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// app/wiki/page.tsx
|
|
83
|
+
export { WikiContentIndex as default, revalidate } from '@ignite-agent/agent/wiki-content'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
// app/wiki/layout.tsx (optional sidebar)
|
|
88
|
+
import { WikiContentLayout } from '@ignite-agent/agent/wiki-content'
|
|
89
|
+
export default function Layout({ children }: { children: React.ReactNode }) {
|
|
90
|
+
return <WikiContentLayout>{children}</WikiContentLayout>
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Sitemap: merge `wikiSitemapEntries()` from `@ignite-agent/agent/wiki-content` into the existing
|
|
95
|
+
`app/sitemap.ts` (with `mergeSitemap` from `@ignite-agent/agent/sitemap` when the host already has one).
|
|
96
|
+
`lastModified` is the entity's last-reviewed date, the same value the footer and JSON-LD show.
|
|
97
|
+
|
|
98
|
+
If `app/w/` already serves Ignite wiki pages at runtime, keep it or remove it with the user; do not run both
|
|
99
|
+
for the same slugs.
|
|
100
|
+
|
|
101
|
+
## 5. Gate the build
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{ "scripts": { "prebuild": "ignite wiki validate" } }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`ignite wiki validate` fails (exit 1, one `BUILD ERROR` line per problem) on: a stat citing an index no
|
|
108
|
+
citation has, a citation URL that is not http(s), a `cross_links` target with no file, duplicate slugs or
|
|
109
|
+
citation indices, a slug that does not match its file name, an empty definition. The page loader applies the
|
|
110
|
+
same schema and throws on drift, so `next build` cannot ship a page the gate refused.
|
|
111
|
+
|
|
112
|
+
## 6. Prove it in the static HTML
|
|
113
|
+
|
|
114
|
+
After `next build`, for one entity:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
curl -s https://<site>/wiki/<slug> | grep -c 'id="definition"' # 1
|
|
118
|
+
curl -s https://<site>/wiki/<slug> | grep -c 'id="cite-1"' # 1 when the page has a citation
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The definition and citations must be in the raw response, not hydrated in. One `<h1>`, no heading skips, one
|
|
122
|
+
`application/ld+json` script per page.
|
|
123
|
+
|
|
124
|
+
## Rules
|
|
125
|
+
|
|
126
|
+
- Server-only. No `"use client"` in the content path. No client fetching.
|
|
127
|
+
- Never invent a stat, a citation, a date, a founder or a `sameAs` URL. Missing means omitted.
|
|
128
|
+
- Never emit ratings, reviews, FAQPage or speakable JSON-LD; there is no data for them.
|
|
129
|
+
- Do not add a CMS, search, comments or revision UI. The PR diff is the accountability surface.
|
|
130
|
+
- Do not present any of this as a ranking lever.
|