@codyswann/lisa 2.24.0 → 2.25.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-plugin/marketplace.json +6 -0
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +8 -0
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +32 -0
- package/plugins/lisa-wiki/ci/lisa-wiki-validate.yml +32 -0
- package/plugins/lisa-wiki/commands/add-ingest.md +6 -0
- package/plugins/lisa-wiki/commands/add-role.md +6 -0
- package/plugins/lisa-wiki/commands/doctor.md +6 -0
- package/plugins/lisa-wiki/commands/ingest.md +6 -0
- package/plugins/lisa-wiki/commands/lint.md +6 -0
- package/plugins/lisa-wiki/commands/migrate.md +6 -0
- package/plugins/lisa-wiki/commands/onboard-me.md +6 -0
- package/plugins/lisa-wiki/commands/query.md +6 -0
- package/plugins/lisa-wiki/commands/setup.md +6 -0
- package/plugins/lisa-wiki/schema/lisa-wiki-config.schema.json +118 -0
- package/plugins/lisa-wiki/schema/wiki-structure.schema.json +51 -0
- package/plugins/lisa-wiki/scripts/_wiki-lib.mjs +185 -0
- package/plugins/lisa-wiki/scripts/diff-guard.mjs +116 -0
- package/plugins/lisa-wiki/scripts/ingest-git.mjs +189 -0
- package/plugins/lisa-wiki/scripts/ingest-memory.mjs +130 -0
- package/plugins/lisa-wiki/scripts/ingest-roles.mjs +85 -0
- package/plugins/lisa-wiki/scripts/ingest_slack_channel.py +329 -0
- package/plugins/lisa-wiki/scripts/lint-wiki.mjs +320 -0
- package/plugins/lisa-wiki/scripts/mcp-doctor.mjs +72 -0
- package/plugins/lisa-wiki/scripts/render-contract.mjs +107 -0
- package/plugins/lisa-wiki/scripts/rewrite-refs.mjs +144 -0
- package/plugins/lisa-wiki/scripts/slack_oauth_user.py +179 -0
- package/plugins/lisa-wiki/scripts/validate-config.mjs +232 -0
- package/plugins/lisa-wiki/scripts/verify-migration.mjs +199 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-add-ingest/SKILL.md +34 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-add-role/SKILL.md +30 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-connector-confluence/SKILL.md +25 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-connector-docs/SKILL.md +30 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-connector-git/SKILL.md +25 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-connector-jira/SKILL.md +28 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-connector-memory/SKILL.md +28 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-connector-notion/SKILL.md +25 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-connector-roles/SKILL.md +22 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-connector-slack/SKILL.md +30 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-connector-web/SKILL.md +23 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-doctor/SKILL.md +47 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-ingest/SKILL.md +43 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-lint/SKILL.md +32 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-migrate/SKILL.md +43 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-onboard-me/SKILL.md +33 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-query/SKILL.md +30 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-setup/SKILL.md +45 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-usage/SKILL.md +50 -0
- package/plugins/lisa-wiki/templates/agents/role-agent.claude.md +16 -0
- package/plugins/lisa-wiki/templates/agents/role-agent.codex.toml +15 -0
- package/plugins/lisa-wiki/templates/index.md +17 -0
- package/plugins/lisa-wiki/templates/llm-wiki-contract.md +60 -0
- package/plugins/lisa-wiki/templates/log.md +8 -0
- package/plugins/lisa-wiki/templates/page-types/architecture.md +18 -0
- package/plugins/lisa-wiki/templates/page-types/concept.md +18 -0
- package/plugins/lisa-wiki/templates/page-types/decision.md +18 -0
- package/plugins/lisa-wiki/templates/page-types/entity.md +19 -0
- package/plugins/lisa-wiki/templates/page-types/open-question.md +18 -0
- package/plugins/lisa-wiki/templates/page-types/playbook.md +18 -0
- package/plugins/lisa-wiki/templates/page-types/project.md +19 -0
- package/plugins/lisa-wiki/templates/page-types/requirement.md +19 -0
- package/plugins/lisa-wiki/templates/page-types/staff.md +26 -0
- package/plugins/lisa-wiki/templates/start-here.md +24 -0
- package/plugins/lisa-wiki/templates/state-readme.md +20 -0
- package/plugins/src/wiki/.claude-plugin/plugin.json +6 -0
- package/plugins/src/wiki/ci/lisa-wiki-validate.yml +32 -0
- package/plugins/src/wiki/commands/add-ingest.md +6 -0
- package/plugins/src/wiki/commands/add-role.md +6 -0
- package/plugins/src/wiki/commands/doctor.md +6 -0
- package/plugins/src/wiki/commands/ingest.md +6 -0
- package/plugins/src/wiki/commands/lint.md +6 -0
- package/plugins/src/wiki/commands/migrate.md +6 -0
- package/plugins/src/wiki/commands/onboard-me.md +6 -0
- package/plugins/src/wiki/commands/query.md +6 -0
- package/plugins/src/wiki/commands/setup.md +6 -0
- package/plugins/src/wiki/schema/lisa-wiki-config.schema.json +118 -0
- package/plugins/src/wiki/schema/wiki-structure.schema.json +51 -0
- package/plugins/src/wiki/scripts/_wiki-lib.mjs +185 -0
- package/plugins/src/wiki/scripts/diff-guard.mjs +116 -0
- package/plugins/src/wiki/scripts/ingest-git.mjs +189 -0
- package/plugins/src/wiki/scripts/ingest-memory.mjs +130 -0
- package/plugins/src/wiki/scripts/ingest-roles.mjs +85 -0
- package/plugins/src/wiki/scripts/ingest_slack_channel.py +329 -0
- package/plugins/src/wiki/scripts/lint-wiki.mjs +320 -0
- package/plugins/src/wiki/scripts/mcp-doctor.mjs +72 -0
- package/plugins/src/wiki/scripts/render-contract.mjs +107 -0
- package/plugins/src/wiki/scripts/rewrite-refs.mjs +144 -0
- package/plugins/src/wiki/scripts/slack_oauth_user.py +179 -0
- package/plugins/src/wiki/scripts/validate-config.mjs +232 -0
- package/plugins/src/wiki/scripts/verify-migration.mjs +199 -0
- package/plugins/src/wiki/skills/lisa-wiki-add-ingest/SKILL.md +34 -0
- package/plugins/src/wiki/skills/lisa-wiki-add-role/SKILL.md +30 -0
- package/plugins/src/wiki/skills/lisa-wiki-connector-confluence/SKILL.md +25 -0
- package/plugins/src/wiki/skills/lisa-wiki-connector-docs/SKILL.md +30 -0
- package/plugins/src/wiki/skills/lisa-wiki-connector-git/SKILL.md +25 -0
- package/plugins/src/wiki/skills/lisa-wiki-connector-jira/SKILL.md +28 -0
- package/plugins/src/wiki/skills/lisa-wiki-connector-memory/SKILL.md +28 -0
- package/plugins/src/wiki/skills/lisa-wiki-connector-notion/SKILL.md +25 -0
- package/plugins/src/wiki/skills/lisa-wiki-connector-roles/SKILL.md +22 -0
- package/plugins/src/wiki/skills/lisa-wiki-connector-slack/SKILL.md +30 -0
- package/plugins/src/wiki/skills/lisa-wiki-connector-web/SKILL.md +23 -0
- package/plugins/src/wiki/skills/lisa-wiki-doctor/SKILL.md +47 -0
- package/plugins/src/wiki/skills/lisa-wiki-ingest/SKILL.md +43 -0
- package/plugins/src/wiki/skills/lisa-wiki-lint/SKILL.md +32 -0
- package/plugins/src/wiki/skills/lisa-wiki-migrate/SKILL.md +43 -0
- package/plugins/src/wiki/skills/lisa-wiki-onboard-me/SKILL.md +33 -0
- package/plugins/src/wiki/skills/lisa-wiki-query/SKILL.md +30 -0
- package/plugins/src/wiki/skills/lisa-wiki-setup/SKILL.md +45 -0
- package/plugins/src/wiki/skills/lisa-wiki-usage/SKILL.md +50 -0
- package/plugins/src/wiki/templates/agents/role-agent.claude.md +16 -0
- package/plugins/src/wiki/templates/agents/role-agent.codex.toml +15 -0
- package/plugins/src/wiki/templates/index.md +17 -0
- package/plugins/src/wiki/templates/llm-wiki-contract.md +60 -0
- package/plugins/src/wiki/templates/log.md +8 -0
- package/plugins/src/wiki/templates/page-types/architecture.md +18 -0
- package/plugins/src/wiki/templates/page-types/concept.md +18 -0
- package/plugins/src/wiki/templates/page-types/decision.md +18 -0
- package/plugins/src/wiki/templates/page-types/entity.md +19 -0
- package/plugins/src/wiki/templates/page-types/open-question.md +18 -0
- package/plugins/src/wiki/templates/page-types/playbook.md +18 -0
- package/plugins/src/wiki/templates/page-types/project.md +19 -0
- package/plugins/src/wiki/templates/page-types/requirement.md +19 -0
- package/plugins/src/wiki/templates/page-types/staff.md +26 -0
- package/plugins/src/wiki/templates/start-here.md +24 -0
- package/plugins/src/wiki/templates/state-readme.md +20 -0
- package/scripts/build-plugins.sh +29 -21
- package/scripts/check-plugins-sync.sh +1 -1
- package/scripts/generate-codex-plugin-artifacts.mjs +22 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* validate-config.mjs — dependency-free validator for wiki/lisa-wiki.config.json.
|
|
4
|
+
*
|
|
5
|
+
* Enforces the constraints described in schema/lisa-wiki-config.schema.json without
|
|
6
|
+
* requiring a JSON-Schema runtime, so it is portable to any downstream repo that
|
|
7
|
+
* installs the lisa-wiki plugin (no ajv / node_modules assumptions).
|
|
8
|
+
*
|
|
9
|
+
* Usage: node validate-config.mjs [path-to-config]
|
|
10
|
+
* default path: wiki/lisa-wiki.config.json (relative to cwd)
|
|
11
|
+
* Exit code 0 = valid, 1 = invalid or unreadable.
|
|
12
|
+
*/
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
|
|
16
|
+
const MODES = ["embedded", "wrapper", "standalone", "subdir"];
|
|
17
|
+
const SIDE_EFFECTS = ["read-only-ingest", "repo-write", "external-write"];
|
|
18
|
+
const RETENTION = [
|
|
19
|
+
"raw-ok",
|
|
20
|
+
"sanitized-note-only",
|
|
21
|
+
"metadata-only",
|
|
22
|
+
"external-pointer-only",
|
|
23
|
+
];
|
|
24
|
+
const SENSITIVITY = ["public", "internal", "confidential", "restricted"];
|
|
25
|
+
const SOURCE_LAYOUT = ["by-system", "by-category"];
|
|
26
|
+
const README_MODE = ["rich", "stub", "preserve"];
|
|
27
|
+
|
|
28
|
+
const configPath = path.resolve(
|
|
29
|
+
process.argv[2] ?? "wiki/lisa-wiki.config.json"
|
|
30
|
+
);
|
|
31
|
+
const errors = [];
|
|
32
|
+
const err = msg => errors.push(msg);
|
|
33
|
+
|
|
34
|
+
function isObject(v) {
|
|
35
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
36
|
+
}
|
|
37
|
+
function isStringArray(v) {
|
|
38
|
+
return Array.isArray(v) && v.every(x => typeof x === "string");
|
|
39
|
+
}
|
|
40
|
+
function checkEnum(value, allowed, label) {
|
|
41
|
+
if (value !== undefined && !allowed.includes(value)) {
|
|
42
|
+
err(`${label}: "${String(value)}" is not one of ${allowed.join(" | ")}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function checkType(value, type, label) {
|
|
46
|
+
if (value !== undefined && typeof value !== type) {
|
|
47
|
+
err(
|
|
48
|
+
`${label}: expected ${type}, got ${Array.isArray(value) ? "array" : typeof value}`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!fs.existsSync(configPath)) {
|
|
54
|
+
console.error(`✗ config not found: ${configPath}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let config;
|
|
59
|
+
try {
|
|
60
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.error(`✗ config is not valid JSON: ${e.message}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!isObject(config)) {
|
|
67
|
+
console.error("✗ config must be a JSON object");
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Required
|
|
72
|
+
for (const key of ["schemaVersion", "org", "mode", "wikiRoot", "categories"]) {
|
|
73
|
+
if (config[key] === undefined) err(`missing required field: ${key}`);
|
|
74
|
+
}
|
|
75
|
+
checkType(config.schemaVersion, "string", "schemaVersion");
|
|
76
|
+
checkType(config.org, "string", "org");
|
|
77
|
+
checkType(config.displayName, "string", "displayName");
|
|
78
|
+
checkType(config.purpose, "string", "purpose");
|
|
79
|
+
checkEnum(config.mode, MODES, "mode");
|
|
80
|
+
checkType(config.wikiRoot, "string", "wikiRoot");
|
|
81
|
+
if (
|
|
82
|
+
typeof config.wikiRoot === "string" &&
|
|
83
|
+
(path.isAbsolute(config.wikiRoot) ||
|
|
84
|
+
config.wikiRoot.split(/[\\/]/).includes(".."))
|
|
85
|
+
) {
|
|
86
|
+
err(
|
|
87
|
+
'wikiRoot: must be a relative path inside the repo (no absolute paths, no ".." traversal)'
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
checkType(config.frontmatter, "boolean", "frontmatter");
|
|
91
|
+
if (
|
|
92
|
+
config.categories !== undefined &&
|
|
93
|
+
!(isStringArray(config.categories) && config.categories.length > 0)
|
|
94
|
+
) {
|
|
95
|
+
err("categories: must be a non-empty array of strings");
|
|
96
|
+
}
|
|
97
|
+
checkEnum(config.sourceRetention, RETENTION, "sourceRetention");
|
|
98
|
+
checkType(config.contaminationTerms, "object", "contaminationTerms");
|
|
99
|
+
if (
|
|
100
|
+
config.contaminationTerms !== undefined &&
|
|
101
|
+
!isStringArray(config.contaminationTerms)
|
|
102
|
+
) {
|
|
103
|
+
err("contaminationTerms: must be an array of strings");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (config.sources !== undefined) {
|
|
107
|
+
if (!isObject(config.sources)) err("sources: must be an object");
|
|
108
|
+
else checkEnum(config.sources.layout, SOURCE_LAYOUT, "sources.layout");
|
|
109
|
+
}
|
|
110
|
+
if (config.git !== undefined) {
|
|
111
|
+
if (!isObject(config.git)) err("git: must be an object");
|
|
112
|
+
else {
|
|
113
|
+
checkType(config.git.prPerIngestion, "boolean", "git.prPerIngestion");
|
|
114
|
+
checkType(config.git.autoMerge, "boolean", "git.autoMerge");
|
|
115
|
+
checkType(config.git.targetBranch, "string", "git.targetBranch");
|
|
116
|
+
checkType(config.git.branchPrefix, "string", "git.branchPrefix");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (config.readme !== undefined) {
|
|
120
|
+
if (!isObject(config.readme)) err("readme: must be an object");
|
|
121
|
+
else checkEnum(config.readme.mode, README_MODE, "readme.mode");
|
|
122
|
+
}
|
|
123
|
+
if (config.sensitivity !== undefined) {
|
|
124
|
+
if (!isObject(config.sensitivity)) err("sensitivity: must be an object");
|
|
125
|
+
else {
|
|
126
|
+
checkType(config.sensitivity.enabled, "boolean", "sensitivity.enabled");
|
|
127
|
+
checkEnum(config.sensitivity.default, SENSITIVITY, "sensitivity.default");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (config.documentation !== undefined) {
|
|
131
|
+
if (!isObject(config.documentation)) err("documentation: must be an object");
|
|
132
|
+
else {
|
|
133
|
+
checkType(config.documentation.absorb, "boolean", "documentation.absorb");
|
|
134
|
+
if (
|
|
135
|
+
config.documentation.keepInPlace !== undefined &&
|
|
136
|
+
!isStringArray(config.documentation.keepInPlace)
|
|
137
|
+
) {
|
|
138
|
+
err("documentation.keepInPlace: must be an array of strings");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (config.onboarding !== undefined) {
|
|
143
|
+
if (!isObject(config.onboarding)) err("onboarding: must be an object");
|
|
144
|
+
else
|
|
145
|
+
checkType(
|
|
146
|
+
config.onboarding.allowAudienceNote,
|
|
147
|
+
"boolean",
|
|
148
|
+
"onboarding.allowAudienceNote"
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (config.connectors !== undefined) {
|
|
153
|
+
if (!isObject(config.connectors))
|
|
154
|
+
err("connectors: must be an object (name -> connector config)");
|
|
155
|
+
else {
|
|
156
|
+
for (const [name, c] of Object.entries(config.connectors)) {
|
|
157
|
+
if (!isObject(c)) {
|
|
158
|
+
err(`connectors.${name}: must be an object`);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
checkType(c.enabled, "boolean", `connectors.${name}.enabled`);
|
|
162
|
+
if (c.sideEffects === undefined) {
|
|
163
|
+
err(
|
|
164
|
+
`connectors.${name}: missing required field "sideEffects" (every connector must declare its side-effect class so full ingest can skip external-write)`
|
|
165
|
+
);
|
|
166
|
+
} else {
|
|
167
|
+
checkEnum(
|
|
168
|
+
c.sideEffects,
|
|
169
|
+
SIDE_EFFECTS,
|
|
170
|
+
`connectors.${name}.sideEffects`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (config.customConnectors !== undefined) {
|
|
178
|
+
if (!Array.isArray(config.customConnectors))
|
|
179
|
+
err("customConnectors: must be an array");
|
|
180
|
+
else {
|
|
181
|
+
config.customConnectors.forEach((c, i) => {
|
|
182
|
+
if (!isObject(c)) {
|
|
183
|
+
err(`customConnectors[${i}]: must be an object`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
for (const k of ["name", "skill", "sourceSystem", "sideEffects"]) {
|
|
187
|
+
if (c[k] === undefined)
|
|
188
|
+
err(`customConnectors[${i}]: missing required field "${k}"`);
|
|
189
|
+
}
|
|
190
|
+
checkEnum(
|
|
191
|
+
c.sideEffects,
|
|
192
|
+
SIDE_EFFECTS,
|
|
193
|
+
`customConnectors[${i}].sideEffects`
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (config.staff !== undefined) {
|
|
200
|
+
if (!Array.isArray(config.staff)) err("staff: must be an array");
|
|
201
|
+
else {
|
|
202
|
+
config.staff.forEach((s, i) => {
|
|
203
|
+
if (!isObject(s)) {
|
|
204
|
+
err(`staff[${i}]: must be an object`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
for (const k of ["id", "role"]) {
|
|
208
|
+
if (s[k] === undefined)
|
|
209
|
+
err(`staff[${i}]: missing required field "${k}"`);
|
|
210
|
+
}
|
|
211
|
+
checkEnum(s.sensitivity, SENSITIVITY, `staff[${i}].sensitivity`);
|
|
212
|
+
if (s.owns !== undefined) {
|
|
213
|
+
if (!isObject(s.owns)) err(`staff[${i}].owns: must be an object`);
|
|
214
|
+
else {
|
|
215
|
+
for (const ok of ["categories", "connectors", "skills"]) {
|
|
216
|
+
if (s.owns[ok] !== undefined && !isStringArray(s.owns[ok])) {
|
|
217
|
+
err(`staff[${i}].owns.${ok}: must be an array of strings`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (errors.length > 0) {
|
|
227
|
+
console.error(`✗ ${path.relative(process.cwd(), configPath)} is invalid:`);
|
|
228
|
+
for (const e of errors) console.error(` - ${e}`);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
console.log(`✓ ${path.relative(process.cwd(), configPath)} is valid.`);
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* verify-migration.mjs — the deterministic half of /doctor. Dependency-free.
|
|
4
|
+
*
|
|
5
|
+
* Composes validate-config + lint-wiki into a grouped report and writes
|
|
6
|
+
* <wikiRoot>/state/migration/doctor-report.json with an overall verdict. Groups D
|
|
7
|
+
* (runtime surfaces) and E (functional smoke) are SKIPPED here — the lisa-wiki-doctor
|
|
8
|
+
* SKILL performs those and merges its results.
|
|
9
|
+
*
|
|
10
|
+
* Usage: node verify-migration.mjs [--wiki <root>] [--config <path>] [--migration]
|
|
11
|
+
* Exit 0 = READY or READY_WITH_WARNINGS, 1 = NOT_READY.
|
|
12
|
+
*/
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { spawnSync } from "node:child_process";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { loadConfig } from "./_wiki-lib.mjs";
|
|
18
|
+
|
|
19
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const argv = process.argv.slice(2);
|
|
21
|
+
const opt = n => {
|
|
22
|
+
const i = argv.indexOf(n);
|
|
23
|
+
return i !== -1 ? argv[i + 1] : undefined;
|
|
24
|
+
};
|
|
25
|
+
const migration = argv.includes("--migration");
|
|
26
|
+
const configPath = opt("--config");
|
|
27
|
+
const { config } = loadConfig(configPath);
|
|
28
|
+
const wikiRoot = path.resolve(opt("--wiki") ?? config?.wikiRoot ?? "wiki");
|
|
29
|
+
|
|
30
|
+
const groups = { A: [], B: [], C: [], D: [], E: [], F: [], G: [] };
|
|
31
|
+
const add = (g, id, status, message) => groups[g].push({ id, status, message });
|
|
32
|
+
|
|
33
|
+
function runNode(script, args) {
|
|
34
|
+
const res = spawnSync("node", [path.join(scriptDir, script), ...args], {
|
|
35
|
+
encoding: "utf8",
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
status: res.status ?? 1,
|
|
39
|
+
stdout: res.stdout ?? "",
|
|
40
|
+
stderr: (res.stderr ?? "") + (res.error ? `\n${res.error.message}` : ""),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// --- A. structure & config ------------------------------------------------
|
|
45
|
+
const vc = runNode("validate-config.mjs", [
|
|
46
|
+
configPath ?? path.join(wikiRoot, "lisa-wiki.config.json"),
|
|
47
|
+
]);
|
|
48
|
+
add(
|
|
49
|
+
"A",
|
|
50
|
+
"config-valid",
|
|
51
|
+
vc.status === 0 ? "PASS" : "FAIL",
|
|
52
|
+
vc.status === 0
|
|
53
|
+
? "config validates"
|
|
54
|
+
: `config invalid: ${(vc.stderr || vc.stdout).trim().split("\n").slice(0, 6).join("; ")}`
|
|
55
|
+
);
|
|
56
|
+
add(
|
|
57
|
+
"A",
|
|
58
|
+
"schema-version",
|
|
59
|
+
config?.schemaVersion ? "PASS" : "FAIL",
|
|
60
|
+
config?.schemaVersion
|
|
61
|
+
? `schemaVersion ${config.schemaVersion}`
|
|
62
|
+
: "schemaVersion missing"
|
|
63
|
+
);
|
|
64
|
+
add(
|
|
65
|
+
"A",
|
|
66
|
+
"readme-mode",
|
|
67
|
+
config?.readme?.mode ? "PASS" : "WARN",
|
|
68
|
+
config?.readme?.mode
|
|
69
|
+
? `readme.mode ${config.readme.mode}`
|
|
70
|
+
: "readme.mode not recorded (asked by /setup)"
|
|
71
|
+
);
|
|
72
|
+
add(
|
|
73
|
+
"A",
|
|
74
|
+
"purpose",
|
|
75
|
+
config?.purpose ? "PASS" : "WARN",
|
|
76
|
+
config?.purpose
|
|
77
|
+
? "purpose set"
|
|
78
|
+
: "purpose not set (asked by /setup; feeds onboarding + contract)"
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// --- A/B. lint (structure -> A, everything else -> B) ---------------------
|
|
82
|
+
const lint = runNode("lint-wiki.mjs", [
|
|
83
|
+
"--wiki",
|
|
84
|
+
wikiRoot,
|
|
85
|
+
...(configPath ? ["--config", configPath] : []),
|
|
86
|
+
"--json",
|
|
87
|
+
]);
|
|
88
|
+
let lintReport;
|
|
89
|
+
try {
|
|
90
|
+
lintReport = JSON.parse(lint.stdout);
|
|
91
|
+
} catch {
|
|
92
|
+
add(
|
|
93
|
+
"B",
|
|
94
|
+
"lint-run",
|
|
95
|
+
"FAIL",
|
|
96
|
+
`lint-wiki did not produce JSON: ${(lint.stderr || lint.stdout).trim().slice(0, 200)}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
if (lintReport) {
|
|
100
|
+
const structureItems = lintReport.items.filter(i => i.group === "structure");
|
|
101
|
+
const otherItems = lintReport.items.filter(i => i.group !== "structure");
|
|
102
|
+
if (structureItems.length === 0)
|
|
103
|
+
add("A", "structure", "PASS", "structure conforms to the manifest");
|
|
104
|
+
for (const i of structureItems)
|
|
105
|
+
add(
|
|
106
|
+
"A",
|
|
107
|
+
`structure:${i.group}`,
|
|
108
|
+
i.status,
|
|
109
|
+
`${i.message}${i.file ? ` (${i.file})` : ""}`
|
|
110
|
+
);
|
|
111
|
+
if (otherItems.length === 0)
|
|
112
|
+
add("B", "integrity", "PASS", "no integrity/safety findings");
|
|
113
|
+
for (const i of otherItems)
|
|
114
|
+
add(
|
|
115
|
+
"B",
|
|
116
|
+
`${i.group}`,
|
|
117
|
+
i.status,
|
|
118
|
+
`${i.message}${i.file ? ` (${i.file})` : ""}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// --- C/D/E/F/G: deterministic-light + delegated to the doctor skill --------
|
|
123
|
+
add(
|
|
124
|
+
"C",
|
|
125
|
+
"no-loss",
|
|
126
|
+
"SKIP",
|
|
127
|
+
"no-loss/parity needs the migration manifest from /migrate; dangling-link check is covered in B"
|
|
128
|
+
);
|
|
129
|
+
add(
|
|
130
|
+
"D",
|
|
131
|
+
"runtime",
|
|
132
|
+
"SKIP",
|
|
133
|
+
"runtime surfaces (commands/skills/subagents/MCP) verified by the lisa-wiki-doctor skill"
|
|
134
|
+
);
|
|
135
|
+
add(
|
|
136
|
+
"E",
|
|
137
|
+
"smoke",
|
|
138
|
+
"SKIP",
|
|
139
|
+
"functional smoke tests (ingest/query/lint/onboard) performed by the lisa-wiki-doctor skill"
|
|
140
|
+
);
|
|
141
|
+
add(
|
|
142
|
+
"F",
|
|
143
|
+
"mode",
|
|
144
|
+
config?.mode ? "PASS" : "FAIL",
|
|
145
|
+
config?.mode ? `mode: ${config.mode}; wikiRoot resolves` : "mode not set"
|
|
146
|
+
);
|
|
147
|
+
add(
|
|
148
|
+
"G",
|
|
149
|
+
"git-ci-dist",
|
|
150
|
+
"SKIP",
|
|
151
|
+
"git/CI/distribution checks performed by the lisa-wiki-doctor skill and CI"
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// --- verdict --------------------------------------------------------------
|
|
155
|
+
const all = Object.values(groups).flat();
|
|
156
|
+
const isBlockingWarn = (item, group) =>
|
|
157
|
+
migration && (group === "A" || group === "B") && item.status === "WARN";
|
|
158
|
+
let verdict = "READY";
|
|
159
|
+
if (all.some(i => i.status === "FAIL")) verdict = "NOT_READY";
|
|
160
|
+
else if (
|
|
161
|
+
Object.entries(groups).some(([g, items]) =>
|
|
162
|
+
items.some(i => isBlockingWarn(i, g))
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
verdict = "NOT_READY";
|
|
166
|
+
else if (all.some(i => i.status === "WARN")) verdict = "READY_WITH_WARNINGS";
|
|
167
|
+
|
|
168
|
+
const reportObj = {
|
|
169
|
+
tool: "verify-migration",
|
|
170
|
+
deterministic: true,
|
|
171
|
+
wikiRoot: path.relative(process.cwd(), wikiRoot) || ".",
|
|
172
|
+
mode: config?.mode ?? null,
|
|
173
|
+
migration,
|
|
174
|
+
generatedAt: new Date().toISOString(),
|
|
175
|
+
verdict,
|
|
176
|
+
subset: "deterministic",
|
|
177
|
+
note: "Deterministic subset only. Groups C (no-loss/parity), D (runtime surfaces), E (functional smoke), and G (git/CI/distribution) are completed by the lisa-wiki-doctor skill, which merges its results into this report.",
|
|
178
|
+
groups,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const outPath = path.join(wikiRoot, "state", "migration", "doctor-report.json");
|
|
182
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
183
|
+
fs.writeFileSync(outPath, `${JSON.stringify(reportObj, null, 2)}\n`);
|
|
184
|
+
|
|
185
|
+
const counts = all.reduce(
|
|
186
|
+
(acc, i) => ((acc[i.status] = (acc[i.status] ?? 0) + 1), acc),
|
|
187
|
+
{}
|
|
188
|
+
);
|
|
189
|
+
console.log(
|
|
190
|
+
`verdict: ${verdict} (${["PASS", "WARN", "FAIL", "SKIP"].map(s => `${counts[s] ?? 0} ${s}`).join(", ")})`
|
|
191
|
+
);
|
|
192
|
+
console.log(`report → ${path.relative(process.cwd(), outPath)}`);
|
|
193
|
+
console.log(
|
|
194
|
+
" (deterministic subset — C/D/E/G completed by the /doctor skill)"
|
|
195
|
+
);
|
|
196
|
+
for (const i of all.filter(i => i.status === "FAIL" || i.status === "WARN")) {
|
|
197
|
+
console.log(` ${i.status === "FAIL" ? "✗" : "⚠"} [${i.id}] ${i.message}`);
|
|
198
|
+
}
|
|
199
|
+
process.exit(verdict === "NOT_READY" ? 1 : 0);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-wiki-add-ingest
|
|
3
|
+
description: Scaffold a project-specific "front-door" ingest skill that does something unique (classify a source, fetch from a special system, stamp domain frontmatter) and then chains into /ingest. Use when a project needs a bespoke ingestion path that the core connectors do not cover — instead of forking the kernel. The generated skill enriches and delegates; the kernel still owns synthesis, index, log, verify, state, and PR.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# lisa-wiki-add-ingest
|
|
7
|
+
|
|
8
|
+
Generate a thin, project-local front-door ingest skill so a project can extend ingestion **without
|
|
9
|
+
forking** the kernel. The front-door does only the unique part, then hands enriched parameters to
|
|
10
|
+
`/ingest`.
|
|
11
|
+
|
|
12
|
+
## Workflow
|
|
13
|
+
1. **Interview** the project: a short name; what the source is; which `wiki/sources/<sourceSystem>/`
|
|
14
|
+
bucket and page type/frontmatter it should produce; whether it merely *enriches/classifies* an
|
|
15
|
+
input or also *fetches* from an external system; and its **side-effect class**
|
|
16
|
+
(`read-only-ingest` | `repo-write` | `external-write`).
|
|
17
|
+
2. **Generate** the front-door skill on both runtimes —
|
|
18
|
+
`.claude/skills/lisa-wiki-local-<name>/SKILL.md` and `.agents/skills/lisa-wiki-local-<name>/SKILL.md`.
|
|
19
|
+
Its body does the unique step, then **delegates to the `lisa-wiki-ingest` skill** (Claude facade
|
|
20
|
+
`/ingest`) passing the bucket/type/metadata. If it fetches, it writes only a sanitized source note
|
|
21
|
+
(+ run metadata) and lets the kernel do synthesis/index/log/verify/state/PR.
|
|
22
|
+
3. **Register** it in `wiki/lisa-wiki.config.json` under `customConnectors`
|
|
23
|
+
(`{ name, skill, sourceSystem, stateFile, sideEffects }`). `/ingest` dispatches **only** to
|
|
24
|
+
registered names — no auto-discovery.
|
|
25
|
+
|
|
26
|
+
## Rules (the front-door contract)
|
|
27
|
+
- A generated front-door writes **only** its source note + run metadata. It must not write synthesis
|
|
28
|
+
pages, `index.md`, `log.md`, or final state — the kernel does those, in order, after it returns.
|
|
29
|
+
- `external-write` front-doors require config opt-in **and** explicit per-run intent, and their PRs
|
|
30
|
+
never auto-merge.
|
|
31
|
+
- Side effects outside the declared class are a hard failure (enforced by the touched-file guard).
|
|
32
|
+
|
|
33
|
+
## Related
|
|
34
|
+
`lisa-wiki-ingest` (what it chains into), `lisa-wiki-add-role`, `lisa-wiki-doctor`.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-wiki-add-role
|
|
3
|
+
description: Scaffold a domain-expert "digital staff" role over the wiki — a dual-runtime subagent (Claude + Codex) plus a staff doc page — from a config.staff[] entry. Use when a project wants a role-scoped expert (e.g. Legal, Finance, Sales) whose knowledge is a slice of the wiki. The plugin only SETS UP the subagent; whether it is ever invoked, scheduled, or routed is out of scope.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# lisa-wiki-add-role
|
|
7
|
+
|
|
8
|
+
Turn a role definition into two generated artifacts: a documentation page in the wiki and a runnable,
|
|
9
|
+
brain-pointed subagent on both runtimes. **Running the subagent (invocation, scheduling, Telegram /
|
|
10
|
+
agent-team routing, private notebooks) is out of scope** — this skill only creates it.
|
|
11
|
+
|
|
12
|
+
## Workflow
|
|
13
|
+
1. **Resolve the role** from a `config.staff[]` entry (or interview to add one): `id`, `role`,
|
|
14
|
+
`expertise`, `owns` (categories / connectors / skills), `sensitivity`.
|
|
15
|
+
2. **Doc page:** generate `wiki/staff/<id>.md` describing the role, its owned domain, and who it
|
|
16
|
+
reports to. This page is wiki content and is itself ingestible (the `roles` connector).
|
|
17
|
+
3. **Subagents (dual-runtime), rendered from the role-agent templates:**
|
|
18
|
+
- Claude: `.claude/agents/<id>.md`.
|
|
19
|
+
- Codex: `.codex/agents/<id>.toml` (keys `name`, `description`, `developer_instructions`; optional
|
|
20
|
+
`model`, `model_reasoning_effort`, `sandbox_mode`).
|
|
21
|
+
4. **Brain-pointed, not baked:** the subagent's instructions say *"your domain is `wiki/<owned>/`;
|
|
22
|
+
`/query` it first, contribute via `/ingest`; stay in your lane."* It points at the live wiki so it
|
|
23
|
+
never goes stale. Only its one-line `description` is synthesized from the wiki at generation time.
|
|
24
|
+
|
|
25
|
+
## Rules
|
|
26
|
+
- v1 instructs lane-keeping but does not *enforce* per-role write isolation (deferred).
|
|
27
|
+
- Setup seeds the starter roster by delegating here per `config.staff[]` entry.
|
|
28
|
+
|
|
29
|
+
## Related
|
|
30
|
+
`lisa-wiki-setup` (seeds the roster), `lisa-wiki-add-ingest`, `lisa-wiki-onboard-me`.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-wiki-connector-confluence
|
|
3
|
+
description: Produce sanitized Confluence source notes for lisa-wiki ingest via the Atlassian MCP. Use only when lisa-wiki-ingest routes to the confluence connector. Read-only; tenant-guarded.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# lisa-wiki-connector-confluence
|
|
7
|
+
|
|
8
|
+
Skill-driven connector (Atlassian MCP). Writes ONLY source notes under `wiki/sources/confluence/` and
|
|
9
|
+
emits a proposed cursor; the kernel does the rest.
|
|
10
|
+
|
|
11
|
+
## Flow
|
|
12
|
+
1. Confirm `connectors.confluence.enabled` and `sideEffects: read-only-ingest`.
|
|
13
|
+
2. **Tenant guard:** verify the Atlassian connection matches `connectors.confluence.tenantGuard`
|
|
14
|
+
(same site/cloudId discipline as jira). Abort on mismatch.
|
|
15
|
+
3. **Window:** read `wiki/state/confluence/*.json`. First run → configured window; incremental →
|
|
16
|
+
pages updated since the cursor. Scope to the configured spaces.
|
|
17
|
+
4. **Fetch (read-only)** page content + metadata via the Atlassian MCP. Never edit Confluence.
|
|
18
|
+
5. **Write source notes** under `wiki/sources/confluence/<YYYY-MM-DD>-confluence-ingest.md` with
|
|
19
|
+
frontmatter and citations (`Source: <space>/<page>`). Redact secrets; honor retention/sensitivity.
|
|
20
|
+
6. **Emit run metadata** (proposed cursor, counts) to the handoff file; return to `lisa-wiki-ingest`.
|
|
21
|
+
|
|
22
|
+
## Rules
|
|
23
|
+
- Abort on tenant mismatch; do not invent pages/decisions; weak evidence → open-questions.
|
|
24
|
+
- Disabled if the Atlassian MCP is absent (`scripts/mcp-doctor.mjs`).
|
|
25
|
+
- Writes only source notes + handoff meta; the kernel advances state.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-wiki-connector-docs
|
|
3
|
+
description: Ingest a local document (PDF, DOCX, Markdown, text) into a sanitized source note for lisa-wiki ingest. Use only when lisa-wiki-ingest routes to the docs connector (a file-path input). Uses available local converters; no heavy bundled dependency.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# lisa-wiki-connector-docs
|
|
7
|
+
|
|
8
|
+
Skill-driven connector. Converts a local document to markdown using whatever converter is available,
|
|
9
|
+
writes a source note, and hands off; the kernel does the rest.
|
|
10
|
+
|
|
11
|
+
## Converters (no bundled heavy dependency)
|
|
12
|
+
- **Markdown / text**: ingest directly.
|
|
13
|
+
- **PDF**: `pdftotext` (Poppler) if available, else `pandoc`.
|
|
14
|
+
- **DOCX**: `pandoc` if available, else `textutil` (macOS).
|
|
15
|
+
If no suitable converter is installed, **skip that source and record a `docs` doctor finding** (the
|
|
16
|
+
file could not be converted) so the user can install a converter — a targeted `/ingest <file>` reports
|
|
17
|
+
the skip rather than pretending success. Missing converters are connector-specific, never a
|
|
18
|
+
plugin-install blocker.
|
|
19
|
+
|
|
20
|
+
## Flow
|
|
21
|
+
1. Confirm `connectors.docs.enabled` and `sideEffects: read-only-ingest`.
|
|
22
|
+
2. Resolve the input file; pick the converter by extension; convert to markdown (read-only).
|
|
23
|
+
3. Write a source note under `wiki/sources/docs/<YYYY-MM-DD>-<slug>.md` with frontmatter
|
|
24
|
+
(`type: source`, dates, `source_system: docs`, original filename) and the converted, reader-safe
|
|
25
|
+
text. Redact secrets; honor `sourceRetention`/`sensitivity`.
|
|
26
|
+
4. Emit run metadata (source-note path) to the handoff file; return to `lisa-wiki-ingest`.
|
|
27
|
+
|
|
28
|
+
## Rules
|
|
29
|
+
- Read-only on the source document; never modify the original.
|
|
30
|
+
- Writes only the source note + handoff meta; the kernel advances state.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-wiki-connector-git
|
|
3
|
+
description: Produce sanitized git/PR-history source notes for lisa-wiki ingest. Use only when lisa-wiki-ingest routes to the git connector (self repo or a registered project). Read-only — never checks out, fetches, or mutates the target repo.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# lisa-wiki-connector-git
|
|
7
|
+
|
|
8
|
+
A universal, deterministic connector backed by `scripts/ingest-git.mjs`. It writes ONLY a source note
|
|
9
|
+
under `wiki/sources/git/` (or a per-project path) and emits a proposed cursor; the kernel performs
|
|
10
|
+
synthesis/index/log/verify/state/PR.
|
|
11
|
+
|
|
12
|
+
## Flow
|
|
13
|
+
1. Confirm `connectors.git` is enabled and `read-only-ingest`.
|
|
14
|
+
2. For the self repo (and each registered project under `projects/`), run:
|
|
15
|
+
```
|
|
16
|
+
node "${PLUGIN_ROOT}/scripts/ingest-git.mjs" --repo <path> --slug <name> \
|
|
17
|
+
--config wiki/lisa-wiki.config.json --source-dir wiki/sources/git \
|
|
18
|
+
--state wiki/state/git/<slug>.json --emit-meta wiki/state/handoff/git-<slug>-<runId>.json
|
|
19
|
+
```
|
|
20
|
+
3. Hand the emitted source-note paths + proposed cursor back to `lisa-wiki-ingest`.
|
|
21
|
+
|
|
22
|
+
## Rules
|
|
23
|
+
- Read-only: only `git log`/`rev-parse`/`gh pr list` against the repo; never checkout/fetch/reset/pull.
|
|
24
|
+
- In wrapper/standalone mode, the registered child repos are read-only inputs and are never staged.
|
|
25
|
+
- Writes only its source note + handoff meta; the kernel advances `wiki/state/git/<slug>.json`.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-wiki-connector-jira
|
|
3
|
+
description: Produce sanitized JIRA source notes for lisa-wiki ingest via the Atlassian MCP. Use only when lisa-wiki-ingest routes to the jira connector. Read-only; tenant-guarded.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# lisa-wiki-connector-jira
|
|
7
|
+
|
|
8
|
+
Skill-driven connector (Atlassian MCP). Writes ONLY source notes under `wiki/sources/jira/` and emits
|
|
9
|
+
a proposed cursor; the kernel does synthesis/index/log/verify/state/PR.
|
|
10
|
+
|
|
11
|
+
## Flow
|
|
12
|
+
1. Confirm `connectors.jira.enabled` and `sideEffects: read-only-ingest`.
|
|
13
|
+
2. **Tenant guard:** verify the active Atlassian connection resolves to
|
|
14
|
+
`connectors.jira.tenantGuard.site` / `cloudId`. If it resolves to a different account/tenant,
|
|
15
|
+
**abort** (do not ingest) — this is the cross-tenant contamination guard.
|
|
16
|
+
3. **Window:** read `wiki/state/jira/*.json`. First run → last 4 months (or configured window);
|
|
17
|
+
incremental → issues `updated >=` the cursor watermark. Scope to `connectors.jira.projects[]`.
|
|
18
|
+
4. **Fetch (read-only)** via the Atlassian MCP (JQL search + issue read). Never write to JIRA.
|
|
19
|
+
5. **Write source notes** under `wiki/sources/jira/<YYYY-MM-DD>-jira-ingest.md` — reader-safe, with
|
|
20
|
+
frontmatter (`type: source`, dates, `source_system: jira`) and citations (`Source: <issue key>`).
|
|
21
|
+
Redact secrets; honor `sourceRetention`/`sensitivity`.
|
|
22
|
+
6. **Emit run metadata** (proposed cursor: latest `updated` watermark, counts) to the handoff file,
|
|
23
|
+
then return to `lisa-wiki-ingest`.
|
|
24
|
+
|
|
25
|
+
## Rules
|
|
26
|
+
- Abort on tenant mismatch; never invent issues/PRs/people; weak evidence → open-questions.
|
|
27
|
+
- If the Atlassian MCP is absent, the connector is disabled (see `scripts/mcp-doctor.mjs`).
|
|
28
|
+
- Writes only source notes + handoff meta; the kernel advances state.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-wiki-connector-memory
|
|
3
|
+
description: Ingest the agent's PROJECT-SCOPED memory into a sanitized source note. Use only when lisa-wiki-ingest routes to the memory connector. NEVER ingests global or unrelated memory — global Codex memory and the Chronicle store are refused.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# lisa-wiki-connector-memory
|
|
7
|
+
|
|
8
|
+
A universal connector backed by `scripts/ingest-memory.mjs`. **Project-scoped only.**
|
|
9
|
+
|
|
10
|
+
## Resolve the memory directory (then pass it explicitly)
|
|
11
|
+
- **Claude**: per-project memory at `~/.claude/projects/<encoded-project-path>/memory/` — inherently
|
|
12
|
+
project-scoped; always eligible.
|
|
13
|
+
- **Codex**: eligible ONLY if a project-scoped memory store exists (e.g. a per-project `CODEX_HOME`).
|
|
14
|
+
The global `~/.codex/memories/` and the Chronicle store are **never** ingested (the script
|
|
15
|
+
hard-refuses them).
|
|
16
|
+
|
|
17
|
+
## Flow
|
|
18
|
+
```
|
|
19
|
+
node "${PLUGIN_ROOT}/scripts/ingest-memory.mjs" --memory-dir <project-scoped-dir> \
|
|
20
|
+
--config wiki/lisa-wiki.config.json --source-dir wiki/sources/memory \
|
|
21
|
+
--state wiki/state/memory/memory.json --emit-meta wiki/state/handoff/memory-<runId>.json
|
|
22
|
+
```
|
|
23
|
+
Hand the source note + proposed cursor back to `lisa-wiki-ingest`.
|
|
24
|
+
|
|
25
|
+
## Rules
|
|
26
|
+
- Secrets are redacted; sensitivity is at least `internal`.
|
|
27
|
+
- If no project-scoped memory exists, ingest nothing rather than reaching for global memory.
|
|
28
|
+
- Writes only its source note + handoff meta; the kernel advances state.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-wiki-connector-notion
|
|
3
|
+
description: Produce sanitized Notion source notes for lisa-wiki ingest via the Notion MCP. Use only when lisa-wiki-ingest routes to the notion connector. Read-only; teamspace-guarded.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# lisa-wiki-connector-notion
|
|
7
|
+
|
|
8
|
+
Skill-driven connector (Notion MCP). Writes ONLY source notes under `wiki/sources/notion/` and emits a
|
|
9
|
+
proposed cursor; the kernel does the rest.
|
|
10
|
+
|
|
11
|
+
## Flow
|
|
12
|
+
1. Confirm `connectors.notion.enabled` and `sideEffects: read-only-ingest`.
|
|
13
|
+
2. **Tenant guard:** verify the Notion connection resolves to
|
|
14
|
+
`connectors.notion.tenantGuard.teamspace` / `teamspaceId`. Abort on a different workspace/teamspace.
|
|
15
|
+
3. **Window:** read `wiki/state/notion/*.json`. First run → configured window; incremental → pages
|
|
16
|
+
edited since the cursor.
|
|
17
|
+
4. **Fetch (read-only)** via the Notion MCP. Never edit Notion.
|
|
18
|
+
5. **Write source notes** under `wiki/sources/notion/<YYYY-MM-DD>-notion-ingest.md` with frontmatter
|
|
19
|
+
and citations (`Source: <notion page>`). Redact secrets; honor retention/sensitivity.
|
|
20
|
+
6. **Emit run metadata** (proposed cursor, counts) to the handoff file; return to `lisa-wiki-ingest`.
|
|
21
|
+
|
|
22
|
+
## Rules
|
|
23
|
+
- Abort on teamspace mismatch; do not invent pages; weak evidence → open-questions.
|
|
24
|
+
- Disabled if the Notion MCP is absent (`scripts/mcp-doctor.mjs`).
|
|
25
|
+
- Writes only source notes + handoff meta; the kernel advances state.
|