@mhosaic/feedback-cli 0.49.0 → 0.50.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 +1 -1
- package/dist/bin.js +6 -7
- package/dist/{build-RCLWV2WL.js → build-AHXPNO76.js} +1 -2
- package/dist/{check-FOJPEE4Q.js → check-UZZFPZMA.js} +3 -4
- package/dist/{chunk-COJ75KUD.js → chunk-45QCNACT.js} +0 -1
- package/dist/{chunk-AZQSQJNQ.js → chunk-CFJPCJAQ.js} +0 -1
- package/dist/{chunk-SSLQOK2Z.js → chunk-GJEJSB2Q.js} +2 -3
- package/dist/{chunk-IHJPCMYF.js → chunk-KHQBWJ4A.js} +0 -1
- package/dist/config-75OC57EE.js +7 -0
- package/dist/{doctor-CTL34U4Y.js → doctor-Z4KZBFBN.js} +1 -2
- package/dist/{eject-VNJVOCQM.js → eject-QFPZ7Z3P.js} +1 -2
- package/dist/{generate-VFRQNPOI.js → generate-CLL5W7SX.js} +3 -4
- package/dist/{init-RS7BKALE.js → init-BK6TPKX6.js} +1 -2
- package/dist/{install-skill-BIBDYLKE.js → install-skill-FZ4YOIHX.js} +79 -10
- package/dist/{qa-YR4GCV6T.js → qa-APZ5CKN5.js} +9 -10
- package/dist/{sitemap-react-QTEAUKN5.js → sitemap-react-IF5PX2PM.js} +2 -3
- package/dist/{sitemap-vue-EJDQTCYM.js → sitemap-vue-LWK6Y4FE.js} +2 -3
- package/dist/{verify-LCXL3WOX.js → verify-24HTNRXV.js} +0 -1
- package/package.json +1 -1
- package/skills/integrate-feedback/SKILL.md +25 -91
- package/skills/integrate-feedback/references/consumer-install.md +3 -3
- package/skills/integrate-feedback/references/verify-install.md +21 -20
- package/dist/bin.js.map +0 -1
- package/dist/build-RCLWV2WL.js.map +0 -1
- package/dist/check-FOJPEE4Q.js.map +0 -1
- package/dist/chunk-AZQSQJNQ.js.map +0 -1
- package/dist/chunk-COJ75KUD.js.map +0 -1
- package/dist/chunk-IHJPCMYF.js.map +0 -1
- package/dist/chunk-SSLQOK2Z.js.map +0 -1
- package/dist/config-JE3QRZVH.js +0 -8
- package/dist/config-JE3QRZVH.js.map +0 -1
- package/dist/doctor-CTL34U4Y.js.map +0 -1
- package/dist/eject-VNJVOCQM.js.map +0 -1
- package/dist/generate-VFRQNPOI.js.map +0 -1
- package/dist/init-RS7BKALE.js.map +0 -1
- package/dist/install-skill-BIBDYLKE.js.map +0 -1
- package/dist/qa-YR4GCV6T.js.map +0 -1
- package/dist/sitemap-react-QTEAUKN5.js.map +0 -1
- package/dist/sitemap-vue-EJDQTCYM.js.map +0 -1
- package/dist/verify-LCXL3WOX.js.map +0 -1
- package/skills/chantier/SKILL.md +0 -141
- package/skills/feedback-close/SKILL.md +0 -66
- package/skills/feedback-fix/SKILL.md +0 -176
- package/skills/feedback-from-meeting/SKILL.md +0 -173
- package/skills/feedback-pull/SKILL.md +0 -74
- package/skills/feedback-watch-merges/SKILL.md +0 -68
- package/skills/integrate-feedback/references/operator-provision.md +0 -397
- package/skills/issue-pull/SKILL.md +0 -49
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ pnpm remove @mhosaic/feedback # or npm/yarn equivalent
|
|
|
91
91
|
|
|
92
92
|
## The guided skill
|
|
93
93
|
|
|
94
|
-
After `install-skill`, run `/integrate-feedback` inside Claude Code.
|
|
94
|
+
After `install-skill`, run `/integrate-feedback` inside Claude Code. Paste the handoff payload from your Mhosaic contact (endpoint, `pk_proj_…` key, allowed origins) as your first message, and the skill walks you through wiring the widget into your specific framework — with explicit checkpoints, framework-aware snippets, and a smoke test that proves the end-to-end path works before declaring done. (Provisioning the project/key itself is a separate, Mhosaic-internal step — not part of this package.)
|
|
95
95
|
|
|
96
96
|
Full details: `skills/integrate-feedback/SKILL.md` in this package, or installed at `~/.claude/skills/integrate-feedback/SKILL.md` after `install-skill`.
|
|
97
97
|
|
package/dist/bin.js
CHANGED
|
@@ -4,27 +4,27 @@
|
|
|
4
4
|
async function main() {
|
|
5
5
|
const [, , cmd, ...args] = process.argv;
|
|
6
6
|
if (!cmd || cmd === "init") {
|
|
7
|
-
const { runInit } = await import("./init-
|
|
7
|
+
const { runInit } = await import("./init-BK6TPKX6.js");
|
|
8
8
|
return runInit(args);
|
|
9
9
|
}
|
|
10
10
|
if (cmd === "doctor") {
|
|
11
|
-
const { runDoctor } = await import("./doctor-
|
|
11
|
+
const { runDoctor } = await import("./doctor-Z4KZBFBN.js");
|
|
12
12
|
return runDoctor(args);
|
|
13
13
|
}
|
|
14
14
|
if (cmd === "eject") {
|
|
15
|
-
const { runEject } = await import("./eject-
|
|
15
|
+
const { runEject } = await import("./eject-QFPZ7Z3P.js");
|
|
16
16
|
return runEject(args);
|
|
17
17
|
}
|
|
18
18
|
if (cmd === "verify") {
|
|
19
|
-
const { runVerify } = await import("./verify-
|
|
19
|
+
const { runVerify } = await import("./verify-24HTNRXV.js");
|
|
20
20
|
return runVerify(args);
|
|
21
21
|
}
|
|
22
22
|
if (cmd === "install-skill") {
|
|
23
|
-
const { runInstallSkill } = await import("./install-skill-
|
|
23
|
+
const { runInstallSkill } = await import("./install-skill-FZ4YOIHX.js");
|
|
24
24
|
return runInstallSkill(args);
|
|
25
25
|
}
|
|
26
26
|
if (cmd === "qa") {
|
|
27
|
-
const { runQa } = await import("./qa-
|
|
27
|
+
const { runQa } = await import("./qa-APZ5CKN5.js");
|
|
28
28
|
return runQa(args);
|
|
29
29
|
}
|
|
30
30
|
if (cmd === "--version" || cmd === "-v") {
|
|
@@ -48,4 +48,3 @@ main().catch((err) => {
|
|
|
48
48
|
`);
|
|
49
49
|
process.exitCode = 1;
|
|
50
50
|
});
|
|
51
|
-
//# sourceMappingURL=bin.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadQaConfig
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KHQBWJ4A.js";
|
|
5
5
|
|
|
6
6
|
// src/qa/build.ts
|
|
7
7
|
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
@@ -562,4 +562,3 @@ export {
|
|
|
562
562
|
buildArtifact,
|
|
563
563
|
runBuild
|
|
564
564
|
};
|
|
565
|
-
//# sourceMappingURL=build-RCLWV2WL.js.map
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
generateSuites,
|
|
4
4
|
serializeSuite
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-GJEJSB2Q.js";
|
|
6
|
+
import "./chunk-CFJPCJAQ.js";
|
|
7
7
|
import {
|
|
8
8
|
loadQaConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-KHQBWJ4A.js";
|
|
10
10
|
|
|
11
11
|
// src/qa/check.ts
|
|
12
12
|
import { execFileSync } from "child_process";
|
|
@@ -339,4 +339,3 @@ export {
|
|
|
339
339
|
runCheck,
|
|
340
340
|
runChecks
|
|
341
341
|
};
|
|
342
|
-
//# sourceMappingURL=check-FOJPEE4Q.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
normalizePattern
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-CFJPCJAQ.js";
|
|
5
5
|
import {
|
|
6
6
|
loadQaConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KHQBWJ4A.js";
|
|
8
8
|
|
|
9
9
|
// src/qa/generate.ts
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs";
|
|
@@ -453,4 +453,3 @@ export {
|
|
|
453
453
|
serializeSuite,
|
|
454
454
|
runGenerate
|
|
455
455
|
};
|
|
456
|
-
//# sourceMappingURL=chunk-SSLQOK2Z.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
detectFramework
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-45QCNACT.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/doctor.ts
|
|
7
7
|
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
@@ -225,4 +225,3 @@ export {
|
|
|
225
225
|
scanWidgetWiring,
|
|
226
226
|
scriptSrcOf
|
|
227
227
|
};
|
|
228
|
-
//# sourceMappingURL=doctor-CTL34U4Y.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
detectFramework
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-45QCNACT.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/eject.ts
|
|
7
7
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -37,4 +37,3 @@ async function runEject(argv) {
|
|
|
37
37
|
export {
|
|
38
38
|
runEject
|
|
39
39
|
};
|
|
40
|
-
//# sourceMappingURL=eject-VNJVOCQM.js.map
|
|
@@ -3,12 +3,11 @@ import {
|
|
|
3
3
|
generateSuites,
|
|
4
4
|
runGenerate,
|
|
5
5
|
serializeSuite
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-GJEJSB2Q.js";
|
|
7
|
+
import "./chunk-CFJPCJAQ.js";
|
|
8
|
+
import "./chunk-KHQBWJ4A.js";
|
|
9
9
|
export {
|
|
10
10
|
generateSuites,
|
|
11
11
|
runGenerate,
|
|
12
12
|
serializeSuite
|
|
13
13
|
};
|
|
14
|
-
//# sourceMappingURL=generate-VFRQNPOI.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
detectFramework
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-45QCNACT.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
7
|
import { existsSync as existsSync3 } from "fs";
|
|
@@ -269,4 +269,3 @@ async function runInit(argv) {
|
|
|
269
269
|
export {
|
|
270
270
|
runInit
|
|
271
271
|
};
|
|
272
|
-
//# sourceMappingURL=init-RS7BKALE.js.map
|
|
@@ -1,21 +1,65 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/commands/install-skill.ts
|
|
4
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
4
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
|
|
5
5
|
import { homedir } from "os";
|
|
6
|
-
import { dirname, join } from "path";
|
|
6
|
+
import { dirname, join, sep } from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
import kleur from "kleur";
|
|
9
9
|
function parseArgs(argv) {
|
|
10
|
-
const out = { force: false, dryRun: false, dest: join(homedir(), ".claude", "skills") };
|
|
10
|
+
const out = { force: false, dryRun: false, dest: join(homedir(), ".claude", "skills"), pruneRetired: false };
|
|
11
11
|
for (let i = 0; i < argv.length; i++) {
|
|
12
12
|
const a = argv[i];
|
|
13
13
|
if (a === "--force" || a === "-f") out.force = true;
|
|
14
14
|
else if (a === "--dry-run") out.dryRun = true;
|
|
15
15
|
else if (a === "--dest") out.dest = argv[++i] ?? out.dest;
|
|
16
|
+
else if (a === "--prune-retired") out.pruneRetired = true;
|
|
16
17
|
}
|
|
17
18
|
return out;
|
|
18
19
|
}
|
|
20
|
+
var RETIRED_SKILLS = [
|
|
21
|
+
"chantier",
|
|
22
|
+
"feedback-close",
|
|
23
|
+
"feedback-fix",
|
|
24
|
+
"feedback-from-meeting",
|
|
25
|
+
"feedback-pull",
|
|
26
|
+
"feedback-watch-merges",
|
|
27
|
+
"issue-pull"
|
|
28
|
+
];
|
|
29
|
+
var RETIRED_MARKER = "mhosaic-feedback";
|
|
30
|
+
function hasSymlinkOnPath(dest, rel) {
|
|
31
|
+
let current = dest;
|
|
32
|
+
for (const part of rel.split(sep)) {
|
|
33
|
+
current = join(current, part);
|
|
34
|
+
if (!existsSync(current)) return false;
|
|
35
|
+
if (lstatSync(current).isSymbolicLink()) return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
function looksLikeOurRetiredSkill(dest, name) {
|
|
40
|
+
const skillMd = join(dest, name, "SKILL.md");
|
|
41
|
+
if (!existsSync(skillMd)) return false;
|
|
42
|
+
const content = readFileSync(skillMd, "utf8");
|
|
43
|
+
const match = content.match(/^name:\s*(.+)$/m);
|
|
44
|
+
if (match?.[1]?.trim() !== name) return false;
|
|
45
|
+
return content.includes(RETIRED_MARKER);
|
|
46
|
+
}
|
|
47
|
+
function scanRetired(dest) {
|
|
48
|
+
const prunable = [];
|
|
49
|
+
const skipped = [];
|
|
50
|
+
for (const name of RETIRED_SKILLS) {
|
|
51
|
+
if (!existsSync(join(dest, name))) continue;
|
|
52
|
+
if (hasSymlinkOnPath(dest, name)) {
|
|
53
|
+
skipped.push({ name, reason: "symlink in the path" });
|
|
54
|
+
} else if (!looksLikeOurRetiredSkill(dest, name)) {
|
|
55
|
+
skipped.push({ name, reason: "not a Mhosaic skill" });
|
|
56
|
+
} else {
|
|
57
|
+
prunable.push(name);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { prunable, skipped };
|
|
61
|
+
}
|
|
62
|
+
var STALE_OWN_FILE = join("integrate-feedback", "references", "operator-provision.md");
|
|
19
63
|
function findSkillsSource() {
|
|
20
64
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
21
65
|
const candidates = [
|
|
@@ -76,6 +120,38 @@ async function runInstallSkill(argv) {
|
|
|
76
120
|
process.stdout.write(kleur.yellow(`\u26A0 ${skipped} file(s) skipped (already exist; use --force to overwrite)
|
|
77
121
|
`));
|
|
78
122
|
}
|
|
123
|
+
const scan = scanRetired(args.dest);
|
|
124
|
+
if (args.pruneRetired) {
|
|
125
|
+
if (scan.prunable.length > 0) {
|
|
126
|
+
if (!args.dryRun) {
|
|
127
|
+
for (const name of scan.prunable) rmSync(join(args.dest, name), { recursive: true, force: true });
|
|
128
|
+
}
|
|
129
|
+
process.stdout.write(
|
|
130
|
+
kleur.yellow(`\u26A0 ${scan.prunable.length} retired skill(s) ${args.dryRun ? "would be " : ""}removed: ${scan.prunable.join(", ")}
|
|
131
|
+
`)
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
for (const { name, reason } of scan.skipped) {
|
|
135
|
+
process.stdout.write(kleur.gray(` left alone: ${name} (${reason})
|
|
136
|
+
`));
|
|
137
|
+
}
|
|
138
|
+
} else if (scan.prunable.length > 0) {
|
|
139
|
+
process.stdout.write(
|
|
140
|
+
kleur.yellow(`\u26A0 These skills are no longer part of this package: ${scan.prunable.join(", ")}
|
|
141
|
+
`)
|
|
142
|
+
);
|
|
143
|
+
process.stdout.write(kleur.gray(" Run with --prune-retired to remove them.\n"));
|
|
144
|
+
}
|
|
145
|
+
if (hasSymlinkOnPath(args.dest, STALE_OWN_FILE)) {
|
|
146
|
+
process.stdout.write(kleur.gray(` left alone: ${STALE_OWN_FILE} (symlink in the path)
|
|
147
|
+
`));
|
|
148
|
+
} else if (existsSync(join(args.dest, STALE_OWN_FILE))) {
|
|
149
|
+
if (!args.dryRun) rmSync(join(args.dest, STALE_OWN_FILE), { force: true });
|
|
150
|
+
process.stdout.write(
|
|
151
|
+
kleur.yellow(`\u26A0 ${args.dryRun ? "would remove" : "removed"} ${STALE_OWN_FILE} (no longer part of this package)
|
|
152
|
+
`)
|
|
153
|
+
);
|
|
154
|
+
}
|
|
79
155
|
process.stdout.write("\n");
|
|
80
156
|
process.stdout.write(kleur.bold("Next:\n"));
|
|
81
157
|
process.stdout.write(` 1. ${kleur.gray("Restart Claude Code if you have it open \u2014 skills are discovered at session start.")}
|
|
@@ -83,15 +159,8 @@ async function runInstallSkill(argv) {
|
|
|
83
159
|
process.stdout.write(` 2. ${kleur.gray("Onboard a new host app: ")}${kleur.cyan("/integrate-feedback")}
|
|
84
160
|
`);
|
|
85
161
|
process.stdout.write(` 3. ${kleur.gray("Add the QA Meter (test-coverage FAB) to a host app: ")}${kleur.cyan("/integrate-qa-meter")}
|
|
86
|
-
`);
|
|
87
|
-
process.stdout.write(` 4. ${kleur.gray("Triage + fix reports: ")}${kleur.cyan("/feedback-pull")} ${kleur.gray("\u2192")} ${kleur.cyan("/feedback-fix")} ${kleur.gray("\u2192")} ${kleur.cyan("/feedback-watch-merges")} ${kleur.gray("\u2192")} ${kleur.cyan("/feedback-close")}
|
|
88
|
-
`);
|
|
89
|
-
process.stdout.write(` 5. ${kleur.gray("Design before development (chantiers): ")}${kleur.cyan("/chantier")}
|
|
90
|
-
`);
|
|
91
|
-
process.stdout.write(` 6. ${kleur.gray("Record a meeting's feedback in each person's name: ")}${kleur.cyan("/feedback-from-meeting")}
|
|
92
162
|
`);
|
|
93
163
|
}
|
|
94
164
|
export {
|
|
95
165
|
runInstallSkill
|
|
96
166
|
};
|
|
97
|
-
//# sourceMappingURL=install-skill-BIBDYLKE.js.map
|
|
@@ -5,16 +5,16 @@ async function runQa(args) {
|
|
|
5
5
|
const [sub, ...rest] = args;
|
|
6
6
|
const commands = {
|
|
7
7
|
sitemap: async () => ({ run: dispatchSitemap }),
|
|
8
|
-
generate: async () => ({ run: (await import("./generate-
|
|
9
|
-
build: async () => ({ run: (await import("./build-
|
|
10
|
-
check: async () => ({ run: (await import("./check-
|
|
8
|
+
generate: async () => ({ run: (await import("./generate-CLL5W7SX.js")).runGenerate }),
|
|
9
|
+
build: async () => ({ run: (await import("./build-AHXPNO76.js")).runBuild }),
|
|
10
|
+
check: async () => ({ run: (await import("./check-UZZFPZMA.js")).runCheck }),
|
|
11
11
|
refresh: async () => ({
|
|
12
12
|
run: async (a) => {
|
|
13
|
-
const { loadQaConfig } = await import("./config-
|
|
13
|
+
const { loadQaConfig } = await import("./config-75OC57EE.js");
|
|
14
14
|
const cfg = loadQaConfig(process.cwd());
|
|
15
15
|
if (cfg.sitemap?.framework) await dispatchSitemap(a);
|
|
16
|
-
await (await import("./generate-
|
|
17
|
-
await (await import("./build-
|
|
16
|
+
await (await import("./generate-CLL5W7SX.js")).runGenerate(a);
|
|
17
|
+
await (await import("./build-AHXPNO76.js")).runBuild(a);
|
|
18
18
|
}
|
|
19
19
|
})
|
|
20
20
|
};
|
|
@@ -27,10 +27,10 @@ async function runQa(args) {
|
|
|
27
27
|
await (await entry()).run(rest);
|
|
28
28
|
}
|
|
29
29
|
async function dispatchSitemap(args) {
|
|
30
|
-
const { loadQaConfig } = await import("./config-
|
|
30
|
+
const { loadQaConfig } = await import("./config-75OC57EE.js");
|
|
31
31
|
const framework = loadQaConfig(process.cwd()).sitemap?.framework;
|
|
32
|
-
if (framework === "vue-router") return (await import("./sitemap-vue-
|
|
33
|
-
if (framework === "react-router") return (await import("./sitemap-react-
|
|
32
|
+
if (framework === "vue-router") return (await import("./sitemap-vue-LWK6Y4FE.js")).runSitemap(args);
|
|
33
|
+
if (framework === "react-router") return (await import("./sitemap-react-IF5PX2PM.js")).runReactSitemap(args);
|
|
34
34
|
process.stderr.write(
|
|
35
35
|
`qa sitemap: unsupported framework ${framework ? `'${framework}'` : "(none set)"} (supported: vue-router, react-router). Set config.sitemap.framework.
|
|
36
36
|
`
|
|
@@ -40,4 +40,3 @@ async function dispatchSitemap(args) {
|
|
|
40
40
|
export {
|
|
41
41
|
runQa
|
|
42
42
|
};
|
|
43
|
-
//# sourceMappingURL=qa-YR4GCV6T.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildSitemap
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-CFJPCJAQ.js";
|
|
5
5
|
import {
|
|
6
6
|
loadQaConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KHQBWJ4A.js";
|
|
8
8
|
|
|
9
9
|
// src/qa/sitemap-react.ts
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
@@ -327,4 +327,3 @@ export {
|
|
|
327
327
|
readJsxRoutes,
|
|
328
328
|
runReactSitemap
|
|
329
329
|
};
|
|
330
|
-
//# sourceMappingURL=sitemap-react-QTEAUKN5.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildSitemap
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-CFJPCJAQ.js";
|
|
5
5
|
import {
|
|
6
6
|
loadQaConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KHQBWJ4A.js";
|
|
8
8
|
|
|
9
9
|
// src/qa/sitemap-vue.ts
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
@@ -240,4 +240,3 @@ export {
|
|
|
240
240
|
extractWithWarnings,
|
|
241
241
|
runSitemap
|
|
242
242
|
};
|
|
243
|
-
//# sourceMappingURL=sitemap-vue-EJDQTCYM.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mhosaic/feedback-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"description": "CLI to install @mhosaic/feedback into a host app, verify the integration, and drop a guided Claude Code skill (/integrate-feedback) into ~/.claude/skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: integrate-feedback
|
|
3
|
-
description: Full guide for
|
|
3
|
+
description: Full guide for installing @mhosaic/feedback into a host app — CLI run, framework-specific wiring, identify() recipes, and a live smoke test — plus every common question a teammate might ask about the integration. Use whenever the user mentions: the Mhosaic feedback widget, adding feedback to an app, installing the widget in a client/app, wiring identify(), CORS errors on the widget, or any troubleshooting around the integration. Equally useful as a slash-command runbook and as a reference Claude reads to answer ad-hoc questions.
|
|
4
4
|
user-invocable: true
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# /integrate-feedback — guided widget
|
|
7
|
+
# /integrate-feedback — guided widget install
|
|
8
8
|
|
|
9
|
-
End-to-end flow for getting the **@mhosaic/feedback** widget into a host app.
|
|
10
|
-
|
|
11
|
-
1. **Operator phase** — provisions Company / Project / `pk_proj_…` key on the Mhosaic backend (https://software-factory-3tbbu.ondigitalocean.app). Run from **inside `feedback-tool-mhosaic`**. The skill drives Chrome at the admin SPA, no filesystem changes. At the end, it outputs a Markdown handoff payload and offers to install itself globally so phase 2 is invokable in the client's repo.
|
|
12
|
-
|
|
13
|
-
2. **Consumer phase** — installs the widget in the host app. Run from **inside the client's app directory** (NOT feedback-tool-mhosaic). The skill detects the framework, runs the CLI's `init`, pastes the right entry-point snippet, wires `identify()` against the client's auth provider, and runs a live smoke test in Chrome.
|
|
9
|
+
End-to-end flow for getting the **@mhosaic/feedback** widget into a host app. Run this from **inside the host app's own directory** (NOT `feedback-tool-mhosaic`). You'll need a handoff payload from your Mhosaic contact first — see the Q&A below if you don't have one yet.
|
|
14
10
|
|
|
15
11
|
This skill is **both** a slash-command runbook AND a reference doc. When the user invokes `/integrate-feedback`, run the procedural flow. When they just ask an integration question, answer from the Q&A below.
|
|
16
12
|
|
|
@@ -19,47 +15,28 @@ This skill is **both** a slash-command runbook AND a reference doc. When the use
|
|
|
19
15
|
## Quick Q&A
|
|
20
16
|
|
|
21
17
|
**"What does this skill do?"**
|
|
22
|
-
|
|
18
|
+
Installs the widget in a host app + runs a smoke test that confirms a report lands in admin. Framework-aware (Vite/Next/Nuxt/Astro/Remix/Vue/SvelteKit/plain HTML). Auth-aware (Auth0/Clerk/Supabase/Firebase/NextAuth/Django/JWT/anonymous).
|
|
23
19
|
|
|
24
20
|
**"Do I need to install anything first?"**
|
|
25
|
-
|
|
26
|
-
- Inside `feedback-tool-mhosaic`: no. The skill is committed under `.claude/skills/integrate-feedback/`; `/integrate-feedback` works directly.
|
|
27
|
-
- In the client's repo: optional. If you want `/integrate-feedback` invokable there too (recommended for the consumer phase), the operator phase will offer to install it globally for you (one shell command, `npx @mhosaic/feedback-cli@latest install-skill`). Skip the install and the consumer phase can still happen — you just run `npx @mhosaic/feedback-cli@latest init …` directly in the client's repo and follow the framework-specific snippet from `references/`.
|
|
21
|
+
No — the skill is installed globally by `npx @mhosaic/feedback-cli@latest install-skill` (or your Mhosaic contact may have installed it for you). If it's not there yet, run that command once, then `/integrate-feedback` works in any project on this machine. You can also skip the skill entirely and run `npx @mhosaic/feedback-cli@latest init …` directly, following the framework-specific snippet from `references/`.
|
|
28
22
|
|
|
29
23
|
**"Where do I run `/integrate-feedback` from?"**
|
|
24
|
+
Inside the host app's own project root — NOT `feedback-tool-mhosaic`. The skill writes to the current directory (`.env.local`, entry-point edits); running it inside `feedback-tool-mhosaic` would install the widget into that monorepo by mistake. The skill detects this (via `package.json` name check) and refuses.
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- Don't run consumer phase inside `feedback-tool-mhosaic` — the CLI would install the widget into our own monorepo. The skill detects this (via `package.json` name check) and refuses.
|
|
34
|
-
|
|
35
|
-
**"What permissions do I need on the backend?"**
|
|
36
|
-
|
|
37
|
-
- Login: Feedback Django group.
|
|
38
|
-
- Create a Project/key under an existing company: Feedback group + Membership for that company.
|
|
39
|
-
- Create a brand-new Company (external client): `is_staff=True` + `is_superuser=True`. Held by the platform maintainers.
|
|
26
|
+
**"I don't have a `pk_proj_…` key yet — where do I get one?"**
|
|
27
|
+
Ask your Mhosaic contact. They provision a Company/Project/key on the backend and send you a Markdown handoff payload (endpoint, key, allowed origins) — paste that as your first message when you run `/integrate-feedback`.
|
|
40
28
|
|
|
41
29
|
**"What's Chrome MCP and why do I need it?"**
|
|
42
|
-
A Claude Code MCP server that lets the skill drive a real Chrome tab
|
|
30
|
+
A Claude Code MCP server that lets the skill drive a real Chrome tab, used here for the smoke test. If MCP isn't configured, the flow degrades to "open the dev URL yourself and confirm visually."
|
|
43
31
|
|
|
44
|
-
**"How does the handoff
|
|
45
|
-
|
|
32
|
+
**"How does the handoff work?"**
|
|
33
|
+
Your Mhosaic contact provisions the project and gives you a Markdown handoff payload (endpoint, `pk_proj_…` key, slug, allowed origins). Paste it as your first message when you run `/integrate-feedback` — see the payload format below.
|
|
46
34
|
|
|
47
35
|
**"My FAB isn't appearing — what's wrong?"**
|
|
48
36
|
The FAB stays hidden until `fb.identify({id: 'something'})` runs with a non-empty `id`. Check `references/identify-snippets.md` for your auth provider's recipe. For testing, hardcode `fb.identify({id: 'test'})` to force-render.
|
|
49
37
|
|
|
50
38
|
**"CORS errors / 403 / origin not allowed."**
|
|
51
|
-
The project's `allowed_origins` doesn't include the URL the request comes from. Usual causes: forgot the dev URL (e.g. `http://localhost:5173`), trailing slash, path included, wildcard (rejected by design).
|
|
52
|
-
|
|
53
|
-
**"External client vs internal Mhosaic app?"**
|
|
54
|
-
|
|
55
|
-
- Internal: use existing companies (`mhosaic`, `arime`, `norag`); create a new project under one. Needs Feedback group + Membership.
|
|
56
|
-
- External: create a brand-new Company. Needs `is_staff=True`. The client can't log into software-factory (it's @mhosaic.com-only SSO); Mhosaic acts as report-viewer for v1.
|
|
57
|
-
|
|
58
|
-
**"Do clients get notifications / digests?"**
|
|
59
|
-
Notifications go to a per-project Google Chat space (new reports, incidents, ✅ closures, Friday weekly digest) — wired by pasting the space's incoming-webhook URL into the project settings ("Notifications Google Chat"). **A project with no webhook falls through to the platform's global room: the client sees nothing.** Wiring it is part of the operator phase (Step 4.5 of `references/operator-provision.md`). Email digests additionally require SMTP env vars on the backend — ask the platform operator.
|
|
60
|
-
|
|
61
|
-
**"Why are the project's Métriques / Journaux tabs empty?"**
|
|
62
|
-
`observability enabled` alone forwards nothing. Metrics need `ObservabilityTarget` rows (POST `targets` to `/projects/<id>/observability/enable/` — operator Step 4.6a) and logs need the `log_destinations` block in the client app's DO spec (`scripts/observability/forward-app-logs.sh` — Step 4.6b, triggers a redeploy). If either was skipped at provisioning time, run Step 4.6 now; both are idempotent.
|
|
39
|
+
The project's `allowed_origins` doesn't include the URL the request comes from. Usual causes: forgot the dev URL (e.g. `http://localhost:5173`), trailing slash, path included, wildcard (rejected by design). Ask your Mhosaic contact to open the admin SPA → Edit project → add the missing origin.
|
|
63
40
|
|
|
64
41
|
**"How do I also enable the QA Meter?"**
|
|
65
42
|
The QA Meter is an opt-in second FAB (half size, stacked above the feedback button) that shows test-coverage status. It's host-served and host-gated — no backend involvement. Build the artifact with `mhosaic-feedback qa refresh`, serve `qa-status.json` as a static asset (or from your own endpoint), then enable it per surface:
|
|
@@ -70,76 +47,34 @@ The QA Meter is an opt-in second FAB (half size, stacked above the feedback butt
|
|
|
70
47
|
Only enable it where you want it (e.g. staging); omit the option in production to ship feedback-only.
|
|
71
48
|
|
|
72
49
|
**"Where's the human-readable doc?"**
|
|
73
|
-
`docs/INTEGRATING.md` in
|
|
50
|
+
`docs/INTEGRATING.md` in the `feedback-tool-mhosaic` repo. Covers the skill, the manual fallback, the CLI command reference, and a troubleshooting matrix.
|
|
74
51
|
|
|
75
52
|
---
|
|
76
53
|
|
|
77
54
|
**"The badge counts feedback from other records / pages — is that a bug?"**
|
|
78
|
-
No — that route was flipped to « Regroupé » in the project's admin **Pages** tab. On grouped routes, every record of the same screen (`/dossier/123`, `/dossier/456` → `/dossier/:id`) counts as one page: badge, hover peek, board default and dedup all follow. Exact is the default; only an operator flip changes it.
|
|
55
|
+
No — that route was flipped to « Regroupé » in the project's admin **Pages** tab. On grouped routes, every record of the same screen (`/dossier/123`, `/dossier/456` → `/dossier/:id`) counts as one page: badge, hover peek, board default and dedup all follow. Exact is the default; only an operator flip changes it. Ask your Mhosaic contact to check or revert it: admin SPA → project → Paramètres → Pages.
|
|
79
56
|
|
|
80
57
|
**"A client says one bug on their form creates a separate report per record and nothing groups."**
|
|
81
|
-
That's the exact-by-default behavior on a form/workflow route. Fix is operator-side, zero client code: open the project's **Pages** tab, find the route pattern (the tab flags form-looking ones with « suggestion : formulaire ? »), flip it to « Regroupé ». Reads regroup instantly across history; fingerprints group for new reports
|
|
58
|
+
That's the exact-by-default behavior on a form/workflow route. Fix is operator-side, zero client code — flag it to your Mhosaic contact: they open the project's **Pages** tab, find the route pattern (the tab flags form-looking ones with « suggestion : formulaire ? »), flip it to « Regroupé ». Reads regroup instantly across history; fingerprints group for new reports. If the route uses slug ids the heuristic can't collapse (`/dossier/acme-corp`), a custom template (`/dossier/:slug`) fixes it.
|
|
82
59
|
|
|
83
60
|
**"Does page grouping need anything in the host app?"**
|
|
84
61
|
No. Patterns are discovered from stored feedback and resolved server-side; the widget just sends its pathname. The only optional host hook is `getCurrentPage()` in the widget config, for apps whose notion of "current page" isn't the URL path. Widgets ≥ 0.47.0 also adapt their copy (« sur ce formulaire ») and board default to the resolved mode.
|
|
85
62
|
|
|
86
|
-
## Step 0 —
|
|
87
|
-
|
|
88
|
-
`AskUserQuestion`:
|
|
89
|
-
|
|
90
|
-
- question: `Which phase are you running?`
|
|
91
|
-
- header: `Phase`
|
|
92
|
-
- options:
|
|
93
|
-
- label: `Provision (operator)`, description: `Create a new Company/Project/key on the Mhosaic backend. Run this from inside feedback-tool-mhosaic.`
|
|
94
|
-
- label: `Install (consumer)`, description: `Install the widget into a host app. Run this from inside the client's app directory, with the handoff payload from the operator phase.`
|
|
95
|
-
|
|
96
|
-
Branch on the answer.
|
|
63
|
+
## Step 0 — Pre-flight
|
|
97
64
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
## Step 0.5 — Pre-flight
|
|
101
|
-
|
|
102
|
-
**For operator phase:** confirm Chrome MCP is configured. Call `mcp__claude-in-chrome__tabs_context_mcp` once. If the tool isn't available, tell the user:
|
|
103
|
-
|
|
104
|
-
> "Operator phase needs Chrome MCP. Two options:
|
|
105
|
-
>
|
|
106
|
-
> 1. Install Chrome MCP (Claude Code → MCP settings → add the chrome connector), then re-run `/integrate-feedback`.
|
|
107
|
-
> 2. Provision manually via `docs/INTEGRATING.md` (open the admin SPA, create the project, mint the key)."
|
|
108
|
-
|
|
109
|
-
Stop. Don't continue without MCP.
|
|
110
|
-
|
|
111
|
-
**For consumer phase:** read `package.json` in the cwd. If its `name` is `@mhosaic/feedback-tool-mhosaic` or any of our own monorepo packages, the user is inside our own repo — refuse and redirect:
|
|
65
|
+
Read `package.json` in the cwd. If its `name` is `@mhosaic/feedback-tool-mhosaic` or any of our own monorepo packages, the user is inside our own repo — refuse and redirect:
|
|
112
66
|
|
|
113
|
-
> "Looks like we're inside `feedback-tool-mhosaic`.
|
|
67
|
+
> "Looks like we're inside `feedback-tool-mhosaic`. This skill writes to the current directory; running it here would install the widget into our own monorepo. Close Claude and re-open in the host app's root, then run `/integrate-feedback` again."
|
|
114
68
|
|
|
115
69
|
Stop. Don't continue.
|
|
116
70
|
|
|
117
71
|
---
|
|
118
72
|
|
|
119
|
-
##
|
|
120
|
-
|
|
121
|
-
Read **`references/operator-provision.md`** and follow it step by step. The phase is **A→Z**: a project leaves this flow with its full 360 view (widget + Chat alerts + metrics + logs + members) or with each gap recorded as an explicit operator decision — never a silent skip.
|
|
122
|
-
|
|
123
|
-
1. Confirm operator is SSO-logged-in to software-factory in Chrome.
|
|
124
|
-
2. Collect: company (existing or new), project name + slug, allowed origins, `share_reports_with_widget`.
|
|
125
|
-
3. Drive Chrome MCP to the admin SPA, refresh the JWT via `/api/auth/token/refresh/`, call `POST /companies/`, `POST /projects/`, `POST /project-keys/create/` from the in-tab `fetch()` (uses the existing cookies). Company create auto-grants the creator an owner Membership.
|
|
126
|
-
4. Capture the plaintext `pk_proj_…` from the keys response.
|
|
127
|
-
5. **Chat notifications (Step 4.5):** create or reuse the client's "«Company» Alerts" Chat space, add an incoming webhook named "Mhosaic Feedback", paste its URL into the project's settings page ("Notifications Google Chat"), fire a `[TEST]` probe and confirm the card lands. Never echo the webhook URL into the chat — it's a credential.
|
|
128
|
-
6. **Observability (Step 4.6, governance-gated):** POST `targets` (`environment` × `component` × DO app id) to `/projects/<id>/observability/enable/` so the Métriques page shows the deployment(s), then run `scripts/observability/forward-app-logs.sh <app-id> <index>` per backend app (triggers a redeploy — verify ACTIVE). Without this step the Métriques/Journaux tabs stay empty forever.
|
|
129
|
-
7. **Members (Step 4.7):** add the responsible Mhosaic owner + any client users; mint an MCP key if the fix-flow is wanted. The company must not end the flow member-less.
|
|
130
|
-
8. Verify the project + key show up in the admin SPA.
|
|
131
|
-
9. **Completeness card (Step 5):** build the "Provisioning 360" checklist; every unchecked line must carry the operator's explicit skip reason. Print it with the handoff payload.
|
|
132
|
-
10. Offer to install the skill globally so the operator can run `/integrate-feedback` in the client's repo for the consumer phase. The full offer logic is at the end of `references/operator-provision.md`.
|
|
133
|
-
|
|
134
|
-
After this phase the operator has: (a) the Provisioning 360 card + handoff payload in the chat, (b) optionally `/integrate-feedback` available globally so the next `claude` in the client repo picks it up.
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
## Consumer phase
|
|
73
|
+
## Steps
|
|
139
74
|
|
|
140
75
|
Read **`references/consumer-install.md`** and follow it step by step:
|
|
141
76
|
|
|
142
|
-
1. Confirm cwd is the
|
|
77
|
+
1. Confirm cwd is the host app's root (NOT feedback-tool-mhosaic — see Step 0).
|
|
143
78
|
2. Parse the pasted handoff payload (endpoint, key, origins).
|
|
144
79
|
3. Detect the framework from `package.json` → route to `references/consumer-install-<framework>.md` (Vite, Next, Nuxt, Astro, Remix, Vue, SvelteKit, or plain/CDN).
|
|
145
80
|
4. Run `npx @mhosaic/feedback-cli@latest init --api-key=… --endpoint=… --yes`.
|
|
@@ -147,15 +82,15 @@ Read **`references/consumer-install.md`** and follow it step by step:
|
|
|
147
82
|
6. Wire `fb.identify()` per `references/identify-snippets.md` — pick the recipe matching the client's auth provider.
|
|
148
83
|
7. Run `mhosaic-feedback verify --origin <dev-url> --with-test-report` — all 6 checks must be green before proceeding.
|
|
149
84
|
8. Start the dev server, drive Chrome to the dev URL, screenshot the FAB, submit a `[INTEGRATE-FEEDBACK SMOKE TEST]` report.
|
|
150
|
-
9.
|
|
85
|
+
9. Confirm the report landed: if you have admin access, drive Chrome to software-factory `/reports`. If you don't (most consumers won't — admin is @mhosaic.com-only SSO), the `201` from step 7's `--with-test-report` is your pass signal — ask your Mhosaic contact to confirm the report landed.
|
|
151
86
|
|
|
152
87
|
Smoke test details + diagnostic table: **`references/verify-install.md`**.
|
|
153
88
|
|
|
154
89
|
---
|
|
155
90
|
|
|
156
|
-
##
|
|
91
|
+
## Handoff payload format (what you'll paste in)
|
|
157
92
|
|
|
158
|
-
|
|
93
|
+
Your Mhosaic contact sends you a block like this — paste it as your first message:
|
|
159
94
|
|
|
160
95
|
````markdown
|
|
161
96
|
## Mhosaic Feedback handoff
|
|
@@ -174,7 +109,7 @@ npx @mhosaic/feedback-cli@latest init \
|
|
|
174
109
|
--yes
|
|
175
110
|
```
|
|
176
111
|
|
|
177
|
-
Or for the full guided flow: `/integrate-feedback` →
|
|
112
|
+
Or for the full guided flow: `/integrate-feedback` → paste this block as your first message.
|
|
178
113
|
````
|
|
179
114
|
|
|
180
115
|
---
|
|
@@ -193,9 +128,8 @@ Or for the full guided flow: `/integrate-feedback` → "Install (consumer)" →
|
|
|
193
128
|
|
|
194
129
|
```
|
|
195
130
|
.claude/skills/integrate-feedback/
|
|
196
|
-
├── SKILL.md # this file
|
|
131
|
+
├── SKILL.md # this file
|
|
197
132
|
├── references/
|
|
198
|
-
│ ├── operator-provision.md # Chrome MCP → admin SPA → DRF
|
|
199
133
|
│ ├── consumer-install.md # framework router
|
|
200
134
|
│ ├── consumer-install-vite.md # Vite + React (CLI auto-wires)
|
|
201
135
|
│ ├── consumer-install-next.md # Next.js App Router + Pages
|