rootkid0-initializer 0.1.0 → 0.1.1
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/.opencode/mcp/README.md +5 -2
- package/README.md +19 -26
- package/bin/rootkid0-initializer.js +31 -46
- package/package.json +3 -3
- package/rootkid0-bootstrap/helpers.sh +3 -29
- package/rootkid0-bootstrap/init-project.ps1 +3 -34
- package/rootkid0-bootstrap/init-project.sh +1 -9
- package/rootkid0-bootstrap/notion-bootstrap.ps1 +87 -31
- package/rootkid0-bootstrap/notion-bootstrap.sh +88 -59
package/.opencode/mcp/README.md
CHANGED
|
@@ -21,13 +21,16 @@ Configura todos los servidores MCP en esta carpeta para evitar definiciones dupl
|
|
|
21
21
|
|
|
22
22
|
## Requisito para init automatico de Notion
|
|
23
23
|
|
|
24
|
-
El bootstrap del proyecto
|
|
24
|
+
El bootstrap del proyecto asume que MCP Notion ya esta preinstalado/configurado por el usuario.
|
|
25
|
+
El initializer NO instala ni modifica MCP global.
|
|
26
|
+
|
|
27
|
+
Validacion minima del bootstrap:
|
|
25
28
|
|
|
26
29
|
- `~/.config/opencode/mcp-servers.json`
|
|
27
30
|
|
|
28
31
|
Condicion minima requerida:
|
|
29
32
|
|
|
30
|
-
- Debe existir entrada `
|
|
33
|
+
- Debe existir entrada `notion` en el archivo global.
|
|
31
34
|
|
|
32
35
|
Si falta, el init falla con mensaje de correccion porque el setup Notion es automatico en P1.
|
|
33
36
|
|
package/README.md
CHANGED
|
@@ -44,51 +44,44 @@ Cada subproyecto trabaja con su `AGENTS.md` local y su `skills/` local.
|
|
|
44
44
|
|
|
45
45
|
### NPX (recomendado)
|
|
46
46
|
|
|
47
|
-
```bash
|
|
48
|
-
npx rootkid0-initializer my-project
|
|
49
|
-
|
|
50
|
-
# Opcional: configurar MCP global baseline
|
|
51
|
-
npx rootkid0-initializer --setup-mcp my-project
|
|
52
|
-
```
|
|
47
|
+
```bash
|
|
48
|
+
npx rootkid0-initializer my-project
|
|
49
|
+
```
|
|
53
50
|
|
|
54
51
|
### Uso directo de scripts (fallback)
|
|
55
52
|
|
|
56
53
|
#### Bash
|
|
57
54
|
|
|
58
|
-
```bash
|
|
55
|
+
```bash
|
|
59
56
|
chmod +x rootkid0-bootstrap/init-project.sh
|
|
60
57
|
./rootkid0-bootstrap/init-project.sh my-project
|
|
61
|
-
|
|
62
|
-
# Opcional: configurar MCP global baseline
|
|
63
|
-
./rootkid0-bootstrap/init-project.sh --setup-mcp my-project
|
|
64
|
-
```
|
|
58
|
+
```
|
|
65
59
|
|
|
66
60
|
#### PowerShell
|
|
67
61
|
|
|
68
|
-
```powershell
|
|
62
|
+
```powershell
|
|
69
63
|
./rootkid0-bootstrap/init-project.ps1 my-project
|
|
70
|
-
|
|
71
|
-
# Opcional: configurar MCP global baseline
|
|
72
|
-
./rootkid0-bootstrap/init-project.ps1 -SetupMcp my-project
|
|
73
|
-
```
|
|
64
|
+
```
|
|
74
65
|
|
|
75
66
|
El script crea una carpeta nueva con la estructura actual del repositorio, excluye `rootkid0-bootstrap/` y `automation/`, reemplaza `{{PROJECT_NAME}}`, mantiene `AGENTS.md`, genera un README inicial y ejecuta bootstrap automatico de Notion.
|
|
76
67
|
|
|
77
68
|
## Setup automatico de Notion (MVP)
|
|
78
69
|
|
|
79
|
-
El init ejecuta bootstrap Notion automaticamente despues de crear el proyecto. Requisitos obligatorios:
|
|
80
|
-
|
|
81
|
-
- MCP
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
|
|
85
|
-
- `NOTION_PARENT_PAGE_ID` (
|
|
86
|
-
- `NOTION_WORKSPACE_NAME` (opcional)
|
|
70
|
+
El init ejecuta bootstrap Notion automaticamente despues de crear el proyecto. Requisitos obligatorios:
|
|
71
|
+
|
|
72
|
+
- MCP Notion preinstalado/configurado por el usuario (el initializer no instala ni modifica MCP global).
|
|
73
|
+
- Archivo global `~/.config/opencode/mcp-servers.json` con entrada `notion`.
|
|
74
|
+
- Credencial de Notion resuelta desde MCP Notion (o `NOTION_TOKEN` si la defines manualmente).
|
|
75
|
+
- Variables opcionales:
|
|
76
|
+
- `NOTION_PARENT_PAGE_ID` (recomendado para crear bajo una raiz definida)
|
|
77
|
+
- `NOTION_WORKSPACE_NAME` (opcional)
|
|
87
78
|
|
|
88
79
|
Resultado del bootstrap:
|
|
89
80
|
|
|
90
|
-
- Crea pagina raiz del proyecto en Notion.
|
|
91
|
-
-
|
|
81
|
+
- Crea pagina raiz del proyecto en Notion.
|
|
82
|
+
- Si existe `NOTION_PARENT_PAGE_ID`, crea la pagina raiz como hija de ese parent.
|
|
83
|
+
- Si no existe `NOTION_PARENT_PAGE_ID`, crea la pagina raiz a nivel workspace del usuario.
|
|
84
|
+
- Crea paginas por fase: `01-business` a `07-production` y `99-common`.
|
|
92
85
|
- Crea secciones placeholder del modelo multi-DB: `Projects`, `Phases`, `Deliverables`, `Backlog`, `Risks`, `Decisions`, `Incidents`.
|
|
93
86
|
- Guarda IDs generados en `99-common/notion-bootstrap.output.json` dentro del proyecto creado.
|
|
94
87
|
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { spawnSync } = require("node:child_process");
|
|
4
|
-
const path = require("node:path");
|
|
5
|
-
|
|
6
|
-
const argv = process.argv.slice(2);
|
|
7
|
-
let projectName = "";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
console.log("
|
|
12
|
-
console.log("");
|
|
13
|
-
console.log("
|
|
14
|
-
|
|
15
|
-
console.log(" npx rootkid0-initializer --setup-mcp my-project");
|
|
16
|
-
}
|
|
3
|
+
const { spawnSync } = require("node:child_process");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
|
|
6
|
+
const argv = process.argv.slice(2);
|
|
7
|
+
let projectName = "";
|
|
8
|
+
|
|
9
|
+
function printHelp() {
|
|
10
|
+
console.log("Uso: rootkid0-initializer <project-name>");
|
|
11
|
+
console.log("");
|
|
12
|
+
console.log("Ejemplos:");
|
|
13
|
+
console.log(" npx rootkid0-initializer my-project");
|
|
14
|
+
}
|
|
17
15
|
|
|
18
16
|
for (const arg of argv) {
|
|
19
17
|
if (arg === "-h" || arg === "--help") {
|
|
@@ -21,15 +19,10 @@ for (const arg of argv) {
|
|
|
21
19
|
process.exit(0);
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
if (arg
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (arg.startsWith("-")) {
|
|
30
|
-
console.error(`Error: opcion no reconocida '${arg}'`);
|
|
31
|
-
printHelp();
|
|
32
|
-
process.exit(1);
|
|
22
|
+
if (arg.startsWith("-")) {
|
|
23
|
+
console.error(`Error: opcion no reconocida '${arg}'`);
|
|
24
|
+
printHelp();
|
|
25
|
+
process.exit(1);
|
|
33
26
|
}
|
|
34
27
|
|
|
35
28
|
if (!projectName) {
|
|
@@ -46,31 +39,23 @@ const packageRoot = path.resolve(__dirname, "..");
|
|
|
46
39
|
let command = "";
|
|
47
40
|
let commandArgs = [];
|
|
48
41
|
|
|
49
|
-
if (process.platform === "win32") {
|
|
42
|
+
if (process.platform === "win32") {
|
|
50
43
|
const psScript = path.join(packageRoot, "rootkid0-bootstrap", "init-project.ps1");
|
|
51
|
-
command = "powershell";
|
|
52
|
-
commandArgs = ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", psScript];
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
commandArgs.push(
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (projectName) {
|
|
59
|
-
commandArgs.push(projectName);
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
44
|
+
command = "powershell";
|
|
45
|
+
commandArgs = ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", psScript];
|
|
46
|
+
|
|
47
|
+
if (projectName) {
|
|
48
|
+
commandArgs.push(projectName);
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
62
51
|
const shScript = path.join(packageRoot, "rootkid0-bootstrap", "init-project.sh");
|
|
63
|
-
command = "bash";
|
|
64
|
-
commandArgs = [shScript];
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
commandArgs.push(
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (projectName) {
|
|
71
|
-
commandArgs.push(projectName);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
52
|
+
command = "bash";
|
|
53
|
+
commandArgs = [shScript];
|
|
54
|
+
|
|
55
|
+
if (projectName) {
|
|
56
|
+
commandArgs.push(projectName);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
74
59
|
|
|
75
60
|
const result = spawnSync(command, commandArgs, {
|
|
76
61
|
stdio: "inherit",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rootkid0-initializer",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "CLI para inicializar proyectos rootkid0
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "CLI para inicializar proyectos rootkid0.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"bin": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"bin/",
|
|
12
|
-
"rootkid0-bootstrap/",
|
|
12
|
+
"rootkid0-bootstrap/",
|
|
13
13
|
".opencode/",
|
|
14
14
|
"01-business/",
|
|
15
15
|
"02-proposal/",
|
|
@@ -49,33 +49,7 @@ replace_project_placeholders() {
|
|
|
49
49
|
done < <(find "$destination" -type f \( -name "*.md" -o -name "*.txt" -o -name "*.json" -o -name "*.yml" -o -name "*.yaml" -o -name "*.ini" -o -name "*.cfg" \) -print0)
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
local repo_root="$1"
|
|
54
|
-
local template_file="$repo_root/.opencode/mcp/servers.template.json"
|
|
55
|
-
local target_dir="$HOME/.config/opencode"
|
|
56
|
-
local target_file="$target_dir/mcp-servers.json"
|
|
57
|
-
local backup_file
|
|
58
|
-
|
|
59
|
-
if [[ ! -f "$template_file" ]]; then
|
|
60
|
-
echo "Aviso: No se encontro $template_file. Se omite setup MCP." >&2
|
|
61
|
-
return 0
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
mkdir -p "$target_dir"
|
|
65
|
-
|
|
66
|
-
if [[ -f "$target_file" ]]; then
|
|
67
|
-
backup_file="$target_file.bak.$(date +%Y%m%d%H%M%S)"
|
|
68
|
-
cp "$target_file" "$backup_file"
|
|
69
|
-
echo "Backup MCP creado: $backup_file"
|
|
70
|
-
fi
|
|
71
|
-
|
|
72
|
-
cp "$template_file" "$target_file"
|
|
73
|
-
echo "MCP global actualizado: $target_file"
|
|
74
|
-
echo "Recomendado: configurar context7, engram y notion usando:"
|
|
75
|
-
echo " $repo_root/.opencode/mcp/servers.recommended.template.json"
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
generate_project_readme() {
|
|
52
|
+
generate_project_readme() {
|
|
79
53
|
local destination="$1"
|
|
80
54
|
local project_name="$2"
|
|
81
55
|
|
|
@@ -90,7 +64,7 @@ Proyecto inicializado desde rootkid0-initializer.
|
|
|
90
64
|
- Plantillas markdown con placeholders ya resueltos para tu proyecto.
|
|
91
65
|
- Configuracion inicial en \`99-common/project.config.json\`.
|
|
92
66
|
- Integracion OpenCode MVP (\`AGENTS.md\`, \`.opencode/\`, AGENTS locales, skills, MCP y agentes por rol).
|
|
93
|
-
- Setup automatico de Notion (MVP):
|
|
67
|
+
- Setup automatico de Notion (MVP): usa MCP Notion preconfigurado por el usuario.
|
|
94
68
|
- Salida de IDs Notion en \`99-common/notion-bootstrap.output.json\`.
|
|
95
69
|
|
|
96
70
|
## Siguientes pasos
|
|
@@ -99,7 +73,7 @@ Proyecto inicializado desde rootkid0-initializer.
|
|
|
99
73
|
2. Ajusta \`99-common/project.config.json\` segun tu stack y contexto.
|
|
100
74
|
3. Revisa \`AGENTS.md\` como entrypoint de roles.
|
|
101
75
|
4. Revisa \`.opencode/README.md\` para el flujo global + subproyectos.
|
|
102
|
-
5.
|
|
76
|
+
5. Confirma prerequisito MCP Notion (preinstalado/configurado) en \`.opencode/mcp/README.md\`.
|
|
103
77
|
6. Verifica \`99-common/notion-bootstrap.output.json\`.
|
|
104
78
|
7. Versiona cambios con Git y define tu backlog inicial.
|
|
105
79
|
EOF
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
param(
|
|
2
2
|
[Parameter(Position = 0)]
|
|
3
|
-
[string]$ProjectName
|
|
4
|
-
[switch]$SetupMcp
|
|
3
|
+
[string]$ProjectName
|
|
5
4
|
)
|
|
6
5
|
|
|
7
6
|
Set-StrictMode -Version Latest
|
|
@@ -45,36 +44,6 @@ Get-ChildItem -Path $destination -Recurse -File |
|
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
$templatePath = Join-Path $repoRoot ".opencode/mcp/servers.template.json"
|
|
49
|
-
$recommendedPath = Join-Path $repoRoot ".opencode/mcp/servers.recommended.template.json"
|
|
50
|
-
|
|
51
|
-
if ($SetupMcp) {
|
|
52
|
-
if (Test-Path $templatePath) {
|
|
53
|
-
$globalMcpDir = Join-Path $HOME ".config/opencode"
|
|
54
|
-
$globalMcpFile = Join-Path $globalMcpDir "mcp-servers.json"
|
|
55
|
-
|
|
56
|
-
New-Item -Path $globalMcpDir -ItemType Directory -Force | Out-Null
|
|
57
|
-
|
|
58
|
-
if (Test-Path $globalMcpFile) {
|
|
59
|
-
$stamp = Get-Date -Format "yyyyMMddHHmmss"
|
|
60
|
-
$backupPath = "$globalMcpFile.bak.$stamp"
|
|
61
|
-
Copy-Item -Path $globalMcpFile -Destination $backupPath -Force
|
|
62
|
-
Write-Host "Backup MCP creado: $backupPath"
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
Copy-Item -Path $templatePath -Destination $globalMcpFile -Force
|
|
66
|
-
Write-Host "MCP global actualizado: $globalMcpFile"
|
|
67
|
-
|
|
68
|
-
if (Test-Path $recommendedPath) {
|
|
69
|
-
Write-Host "Recomendado: configurar context7, engram y notion usando:"
|
|
70
|
-
Write-Host " $recommendedPath"
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
Write-Warning "No se encontro plantilla MCP en $templatePath. Se omite setup MCP."
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
47
|
& (Join-Path $scriptDir "notion-bootstrap.ps1") -ProjectName $ProjectName -ProjectDir $destination
|
|
79
48
|
|
|
80
49
|
$readmePath = Join-Path $destination "README.md"
|
|
@@ -89,7 +58,7 @@ Proyecto inicializado desde rootkid0-initializer.
|
|
|
89
58
|
- Plantillas markdown con placeholders ya resueltos para tu proyecto.
|
|
90
59
|
- Configuracion inicial en `99-common/project.config.json`.
|
|
91
60
|
- Integracion OpenCode MVP (`AGENTS.md`, `.opencode/`, AGENTS locales, skills, MCP y agentes por rol).
|
|
92
|
-
- Setup automatico de Notion (MVP):
|
|
61
|
+
- Setup automatico de Notion (MVP): usa MCP Notion preconfigurado por el usuario.
|
|
93
62
|
- Salida de IDs Notion en `99-common/notion-bootstrap.output.json`.
|
|
94
63
|
|
|
95
64
|
## Siguientes pasos
|
|
@@ -98,7 +67,7 @@ Proyecto inicializado desde rootkid0-initializer.
|
|
|
98
67
|
2. Ajusta `99-common/project.config.json` segun tu stack y contexto.
|
|
99
68
|
3. Revisa `AGENTS.md` como entrypoint de roles.
|
|
100
69
|
4. Revisa `.opencode/README.md` para el flujo global + subproyectos.
|
|
101
|
-
5.
|
|
70
|
+
5. Confirma prerequisito MCP Notion (preinstalado/configurado) en `.opencode/mcp/README.md`.
|
|
102
71
|
6. Verifica `99-common/notion-bootstrap.output.json`.
|
|
103
72
|
7. Versiona cambios con Git y define tu backlog inicial.
|
|
104
73
|
"@
|
|
@@ -8,17 +8,13 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
8
8
|
source "$SCRIPT_DIR/helpers.sh"
|
|
9
9
|
|
|
10
10
|
print_usage() {
|
|
11
|
-
echo "Uso: ./rootkid0-bootstrap/init-project.sh
|
|
11
|
+
echo "Uso: ./rootkid0-bootstrap/init-project.sh <project-name>"
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
SETUP_MCP=false
|
|
15
14
|
PROJECT_NAME=""
|
|
16
15
|
|
|
17
16
|
for arg in "$@"; do
|
|
18
17
|
case "$arg" in
|
|
19
|
-
--setup-mcp)
|
|
20
|
-
SETUP_MCP=true
|
|
21
|
-
;;
|
|
22
18
|
-h|--help)
|
|
23
19
|
print_usage
|
|
24
20
|
exit 0
|
|
@@ -55,10 +51,6 @@ copy_baseline "$REPO_ROOT" "$DESTINATION"
|
|
|
55
51
|
replace_project_placeholders "$DESTINATION" "$PROJECT_NAME"
|
|
56
52
|
generate_project_readme "$DESTINATION" "$PROJECT_NAME"
|
|
57
53
|
|
|
58
|
-
if [[ "$SETUP_MCP" == "true" ]]; then
|
|
59
|
-
setup_global_mcp_config "$REPO_ROOT"
|
|
60
|
-
fi
|
|
61
|
-
|
|
62
54
|
bash "$SCRIPT_DIR/notion-bootstrap.sh" --project-name "$PROJECT_NAME" --project-dir "$DESTINATION"
|
|
63
55
|
|
|
64
56
|
echo
|
|
@@ -20,30 +20,80 @@ function Require-Env([string]$Name) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
function Resolve-NotionToken([string]$McpFile) {
|
|
24
|
+
$token = [Environment]::GetEnvironmentVariable("NOTION_TOKEN")
|
|
25
|
+
if (-not [string]::IsNullOrWhiteSpace($token)) {
|
|
26
|
+
return $token
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
$raw = Get-Content -Path $McpFile -Raw | ConvertFrom-Json
|
|
31
|
+
$fromConfig = $raw.servers.notion.env.NOTION_TOKEN
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
$fromConfig = ""
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if ($fromConfig -is [string]) {
|
|
38
|
+
if ($fromConfig -match '^\$\{(.+)\}$') {
|
|
39
|
+
$refVar = $Matches[1]
|
|
40
|
+
$resolved = [Environment]::GetEnvironmentVariable($refVar)
|
|
41
|
+
if (-not [string]::IsNullOrWhiteSpace($resolved)) {
|
|
42
|
+
return $resolved
|
|
34
43
|
}
|
|
35
44
|
}
|
|
36
|
-
|
|
45
|
+
elseif (-not [string]::IsNullOrWhiteSpace($fromConfig)) {
|
|
46
|
+
return $fromConfig
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
Fail "No se pudo resolver credencial de Notion. Define NOTION_TOKEN o configura servers.notion.env.NOTION_TOKEN en $McpFile."
|
|
37
51
|
}
|
|
38
52
|
|
|
39
|
-
function
|
|
53
|
+
function Get-NotionPagePayload([string]$ParentMode, [string]$ParentValue, [string]$Title) {
|
|
54
|
+
if ($ParentMode -eq "page") {
|
|
55
|
+
return @{
|
|
56
|
+
parent = @{ page_id = $ParentValue }
|
|
57
|
+
properties = @{
|
|
58
|
+
title = @{
|
|
59
|
+
title = @(
|
|
60
|
+
@{
|
|
61
|
+
type = "text"
|
|
62
|
+
text = @{ content = $Title }
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} | ConvertTo-Json -Depth 10
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if ($ParentMode -eq "workspace") {
|
|
71
|
+
return @{
|
|
72
|
+
parent = @{ workspace = $true }
|
|
73
|
+
properties = @{
|
|
74
|
+
title = @{
|
|
75
|
+
title = @(
|
|
76
|
+
@{
|
|
77
|
+
type = "text"
|
|
78
|
+
text = @{ content = $Title }
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} | ConvertTo-Json -Depth 10
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Fail "Modo de parent no soportado para Notion payload: $ParentMode"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function New-NotionPage([string]$ParentMode, [string]$ParentValue, [string]$Title) {
|
|
40
90
|
$headers = @{
|
|
41
|
-
Authorization = "Bearer $
|
|
91
|
+
Authorization = "Bearer $script:NotionToken"
|
|
42
92
|
"Notion-Version" = "2022-06-28"
|
|
43
93
|
"Content-Type" = "application/json"
|
|
44
94
|
}
|
|
45
95
|
|
|
46
|
-
$body = Get-NotionPagePayload -
|
|
96
|
+
$body = Get-NotionPagePayload -ParentMode $ParentMode -ParentValue $ParentValue -Title $Title
|
|
47
97
|
|
|
48
98
|
try {
|
|
49
99
|
$response = Invoke-RestMethod -Method Post -Uri "https://api.notion.com/v1/pages" -Headers $headers -Body $body
|
|
@@ -70,24 +120,17 @@ if (-not (Test-Path -LiteralPath $ProjectDir)) {
|
|
|
70
120
|
$mcpFile = Join-Path $HOME ".config/opencode/mcp-servers.json"
|
|
71
121
|
|
|
72
122
|
if (-not (Test-Path -LiteralPath $mcpFile)) {
|
|
73
|
-
Fail "
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
$mcpConfig = Get-Content -Path $mcpFile -Raw | ConvertFrom-Json
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
Fail "El archivo $mcpFile no tiene JSON valido. Corrigelo y vuelve a ejecutar init-project."
|
|
123
|
+
Fail "Prerequisito faltante: MCP Notion no disponible. Debe existir $mcpFile con entrada notion antes de ejecutar init-project."
|
|
81
124
|
}
|
|
82
125
|
|
|
83
|
-
if (
|
|
84
|
-
Fail "
|
|
126
|
+
if (-not (Select-String -Path $mcpFile -Pattern '"notion"' -Quiet)) {
|
|
127
|
+
Fail "Prerequisito faltante: MCP Notion no disponible. Agrega entrada notion en $mcpFile y vuelve a ejecutar init-project."
|
|
85
128
|
}
|
|
86
129
|
|
|
87
|
-
|
|
88
|
-
Require-Env "NOTION_PARENT_PAGE_ID"
|
|
130
|
+
$script:NotionToken = Resolve-NotionToken -McpFile $mcpFile
|
|
89
131
|
|
|
90
132
|
$workspaceName = [Environment]::GetEnvironmentVariable("NOTION_WORKSPACE_NAME")
|
|
133
|
+
$parentPageId = [Environment]::GetEnvironmentVariable("NOTION_PARENT_PAGE_ID")
|
|
91
134
|
|
|
92
135
|
Write-Host "Iniciando bootstrap automatico de Notion..."
|
|
93
136
|
|
|
@@ -96,7 +139,19 @@ if (-not [string]::IsNullOrWhiteSpace($workspaceName)) {
|
|
|
96
139
|
$rootTitle = "$workspaceName - $ProjectName"
|
|
97
140
|
}
|
|
98
141
|
|
|
99
|
-
$
|
|
142
|
+
$parentMode = "workspace"
|
|
143
|
+
$parentValue = ""
|
|
144
|
+
|
|
145
|
+
if (-not [string]::IsNullOrWhiteSpace($parentPageId)) {
|
|
146
|
+
$parentMode = "page"
|
|
147
|
+
$parentValue = $parentPageId
|
|
148
|
+
Write-Host "Modo parent Notion: page ($parentPageId)"
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
Write-Host "Modo parent Notion: workspace (NOTION_PARENT_PAGE_ID no definido)"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
$projectRootPageId = New-NotionPage -ParentMode $parentMode -ParentValue $parentValue -Title $rootTitle
|
|
100
155
|
Write-Host "Pagina raiz creada: $projectRootPageId"
|
|
101
156
|
|
|
102
157
|
$phaseNames = @(
|
|
@@ -114,7 +169,7 @@ $phaseMap = [ordered]@{}
|
|
|
114
169
|
$commonPageId = ""
|
|
115
170
|
|
|
116
171
|
foreach ($phase in $phaseNames) {
|
|
117
|
-
$phaseId = New-NotionPage -
|
|
172
|
+
$phaseId = New-NotionPage -ParentMode "page" -ParentValue $projectRootPageId -Title $phase
|
|
118
173
|
$phaseMap[$phase] = $phaseId
|
|
119
174
|
Write-Host "Pagina creada ($phase): $phaseId"
|
|
120
175
|
if ($phase -eq "99-common") {
|
|
@@ -139,7 +194,7 @@ $sectionNames = @(
|
|
|
139
194
|
$sectionMap = [ordered]@{}
|
|
140
195
|
|
|
141
196
|
foreach ($section in $sectionNames) {
|
|
142
|
-
$sectionId = New-NotionPage -
|
|
197
|
+
$sectionId = New-NotionPage -ParentMode "page" -ParentValue $commonPageId -Title $section
|
|
143
198
|
$sectionMap[$section] = $sectionId
|
|
144
199
|
Write-Host "Seccion modelo MVP creada ($section): $sectionId"
|
|
145
200
|
}
|
|
@@ -150,7 +205,8 @@ $result = [ordered]@{
|
|
|
150
205
|
project_name = $ProjectName
|
|
151
206
|
workspace_name = $workspaceName
|
|
152
207
|
created_at_utc = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
153
|
-
|
|
208
|
+
notion_parent_mode = $parentMode
|
|
209
|
+
notion_parent_page_id = $parentPageId
|
|
154
210
|
project_root_page_id = $projectRootPageId
|
|
155
211
|
phase_pages = $phaseMap
|
|
156
212
|
model_sections = $sectionMap
|
|
@@ -36,56 +36,13 @@ pick_python() {
|
|
|
36
36
|
|
|
37
37
|
validate_mcp_config() {
|
|
38
38
|
local mcp_file="$HOME/.config/opencode/mcp-servers.json"
|
|
39
|
-
local py_bin
|
|
40
|
-
local py_status
|
|
41
39
|
|
|
42
40
|
if [[ ! -f "$mcp_file" ]]; then
|
|
43
|
-
fail "
|
|
44
|
-
fi
|
|
45
|
-
|
|
46
|
-
py_bin="$(pick_python || true)"
|
|
47
|
-
|
|
48
|
-
if [[ -n "$py_bin" ]]; then
|
|
49
|
-
if "$py_bin" - "$mcp_file" <<'PY'; then
|
|
50
|
-
import json
|
|
51
|
-
import sys
|
|
52
|
-
|
|
53
|
-
path = sys.argv[1]
|
|
54
|
-
try:
|
|
55
|
-
with open(path, "r", encoding="utf-8") as f:
|
|
56
|
-
data = json.load(f)
|
|
57
|
-
except Exception:
|
|
58
|
-
sys.exit(2)
|
|
59
|
-
|
|
60
|
-
servers = data.get("servers")
|
|
61
|
-
if isinstance(servers, dict) and "notion" in servers:
|
|
62
|
-
sys.exit(0)
|
|
63
|
-
|
|
64
|
-
sys.exit(1)
|
|
65
|
-
PY
|
|
66
|
-
py_status=0
|
|
67
|
-
else
|
|
68
|
-
py_status=$?
|
|
69
|
-
fi
|
|
70
|
-
|
|
71
|
-
if [[ "$py_status" -ne 0 ]]; then
|
|
72
|
-
case "$py_status" in
|
|
73
|
-
1)
|
|
74
|
-
fail "Falta el servidor 'notion' en $mcp_file. Agrega la entrada en servers.notion y vuelve a ejecutar init-project."
|
|
75
|
-
;;
|
|
76
|
-
2)
|
|
77
|
-
fail "El archivo $mcp_file no tiene JSON valido. Corrigelo y vuelve a ejecutar init-project."
|
|
78
|
-
;;
|
|
79
|
-
*)
|
|
80
|
-
fail "No se pudo validar $mcp_file. Verifica permisos y formato del archivo."
|
|
81
|
-
;;
|
|
82
|
-
esac
|
|
83
|
-
fi
|
|
84
|
-
return 0
|
|
41
|
+
fail "Prerequisito faltante: MCP Notion no disponible. Debe existir $mcp_file con entrada 'notion' antes de ejecutar init-project."
|
|
85
42
|
fi
|
|
86
43
|
|
|
87
44
|
if ! grep -q '"notion"' "$mcp_file"; then
|
|
88
|
-
fail "
|
|
45
|
+
fail "Prerequisito faltante: MCP Notion no disponible. Agrega la entrada 'notion' en $mcp_file y vuelve a ejecutar init-project."
|
|
89
46
|
fi
|
|
90
47
|
}
|
|
91
48
|
|
|
@@ -105,30 +62,90 @@ require_command() {
|
|
|
105
62
|
fi
|
|
106
63
|
}
|
|
107
64
|
|
|
65
|
+
resolve_notion_token() {
|
|
66
|
+
local mcp_file="$HOME/.config/opencode/mcp-servers.json"
|
|
67
|
+
local token="${NOTION_TOKEN:-}"
|
|
68
|
+
local py_bin
|
|
69
|
+
|
|
70
|
+
if [[ -n "$token" ]]; then
|
|
71
|
+
printf '%s' "$token"
|
|
72
|
+
return 0
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
py_bin="$(pick_python || true)"
|
|
76
|
+
if [[ -n "$py_bin" ]]; then
|
|
77
|
+
token="$($py_bin - "$mcp_file" <<'PY'
|
|
78
|
+
import json
|
|
79
|
+
import os
|
|
80
|
+
import re
|
|
81
|
+
import sys
|
|
82
|
+
|
|
83
|
+
with open(sys.argv[1], "r", encoding="utf-8") as f:
|
|
84
|
+
data = json.load(f)
|
|
85
|
+
|
|
86
|
+
value = ""
|
|
87
|
+
servers = data.get("servers", {})
|
|
88
|
+
notion = servers.get("notion", {}) if isinstance(servers, dict) else {}
|
|
89
|
+
env = notion.get("env", {}) if isinstance(notion, dict) else {}
|
|
90
|
+
|
|
91
|
+
if isinstance(env, dict):
|
|
92
|
+
raw = env.get("NOTION_TOKEN", "")
|
|
93
|
+
if isinstance(raw, str):
|
|
94
|
+
match = re.fullmatch(r"\$\{([^}]+)\}", raw.strip())
|
|
95
|
+
if match:
|
|
96
|
+
value = os.environ.get(match.group(1), "")
|
|
97
|
+
else:
|
|
98
|
+
value = raw
|
|
99
|
+
|
|
100
|
+
print(value)
|
|
101
|
+
PY
|
|
102
|
+
)"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
if [[ -z "$token" ]]; then
|
|
106
|
+
fail "No se pudo resolver credencial de Notion. Define NOTION_TOKEN o configura servers.notion.env.NOTION_TOKEN en $mcp_file."
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
printf '%s' "$token"
|
|
110
|
+
}
|
|
111
|
+
|
|
108
112
|
build_page_payload() {
|
|
109
|
-
local
|
|
110
|
-
local
|
|
113
|
+
local parent_mode="$1"
|
|
114
|
+
local parent_value="$2"
|
|
115
|
+
local title="$3"
|
|
116
|
+
|
|
117
|
+
if [[ "$parent_mode" == "page" ]]; then
|
|
118
|
+
printf '{"parent":{"page_id":"%s"},"properties":{"title":{"title":[{"type":"text","text":{"content":"%s"}}]}}}' \
|
|
119
|
+
"$parent_value" \
|
|
120
|
+
"$(json_escape "$title")"
|
|
121
|
+
return 0
|
|
122
|
+
fi
|
|
111
123
|
|
|
112
|
-
|
|
113
|
-
"
|
|
114
|
-
|
|
124
|
+
if [[ "$parent_mode" == "workspace" ]]; then
|
|
125
|
+
printf '{"parent":{"workspace":true},"properties":{"title":{"title":[{"type":"text","text":{"content":"%s"}}]}}}' \
|
|
126
|
+
"$(json_escape "$title")"
|
|
127
|
+
return 0
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
fail "Modo de parent no soportado para Notion payload: $parent_mode"
|
|
115
131
|
}
|
|
116
132
|
|
|
117
133
|
create_notion_page() {
|
|
118
|
-
local
|
|
119
|
-
local
|
|
134
|
+
local parent_mode="$1"
|
|
135
|
+
local parent_value="$2"
|
|
136
|
+
local title="$3"
|
|
120
137
|
local payload
|
|
121
138
|
local response_file
|
|
122
139
|
local status
|
|
123
140
|
local py_bin
|
|
124
141
|
local page_id
|
|
125
142
|
|
|
126
|
-
payload="$(build_page_payload "$
|
|
143
|
+
payload="$(build_page_payload "$parent_mode" "$parent_value" "$title")"
|
|
127
144
|
response_file="$(mktemp)"
|
|
128
145
|
|
|
129
146
|
status="$(curl -sS -o "$response_file" -w "%{http_code}" \
|
|
130
147
|
-X POST "https://api.notion.com/v1/pages" \
|
|
131
|
-
-H "Authorization: Bearer $
|
|
148
|
+
-H "Authorization: Bearer $NOTION_AUTH_TOKEN" \
|
|
132
149
|
-H "Notion-Version: 2022-06-28" \
|
|
133
150
|
-H "Content-Type: application/json" \
|
|
134
151
|
--data "$payload")"
|
|
@@ -199,10 +216,10 @@ fi
|
|
|
199
216
|
|
|
200
217
|
validate_mcp_config
|
|
201
218
|
require_command "curl"
|
|
202
|
-
|
|
203
|
-
require_env "NOTION_PARENT_PAGE_ID"
|
|
219
|
+
NOTION_AUTH_TOKEN="$(resolve_notion_token)"
|
|
204
220
|
|
|
205
221
|
NOTION_WORKSPACE_NAME="${NOTION_WORKSPACE_NAME:-}"
|
|
222
|
+
NOTION_PARENT_PAGE_ID="${NOTION_PARENT_PAGE_ID:-}"
|
|
206
223
|
|
|
207
224
|
echo "Iniciando bootstrap automatico de Notion..."
|
|
208
225
|
|
|
@@ -211,7 +228,18 @@ if [[ -n "$NOTION_WORKSPACE_NAME" ]]; then
|
|
|
211
228
|
root_title="$NOTION_WORKSPACE_NAME - $PROJECT_NAME"
|
|
212
229
|
fi
|
|
213
230
|
|
|
214
|
-
|
|
231
|
+
parent_mode="workspace"
|
|
232
|
+
parent_value=""
|
|
233
|
+
|
|
234
|
+
if [[ -n "$NOTION_PARENT_PAGE_ID" ]]; then
|
|
235
|
+
parent_mode="page"
|
|
236
|
+
parent_value="$NOTION_PARENT_PAGE_ID"
|
|
237
|
+
echo "Modo parent Notion: page ($NOTION_PARENT_PAGE_ID)"
|
|
238
|
+
else
|
|
239
|
+
echo "Modo parent Notion: workspace (NOTION_PARENT_PAGE_ID no definido)"
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
project_root_page_id="$(create_notion_page "$parent_mode" "$parent_value" "$root_title")"
|
|
215
243
|
echo "Pagina raiz creada: $project_root_page_id"
|
|
216
244
|
|
|
217
245
|
phase_names=(
|
|
@@ -229,7 +257,7 @@ phase_ids=()
|
|
|
229
257
|
common_page_id=""
|
|
230
258
|
|
|
231
259
|
for phase in "${phase_names[@]}"; do
|
|
232
|
-
phase_id="$(create_notion_page "$project_root_page_id" "$phase")"
|
|
260
|
+
phase_id="$(create_notion_page "page" "$project_root_page_id" "$phase")"
|
|
233
261
|
phase_ids+=("$phase_id")
|
|
234
262
|
echo "Pagina creada ($phase): $phase_id"
|
|
235
263
|
if [[ "$phase" == "99-common" ]]; then
|
|
@@ -253,7 +281,7 @@ section_names=(
|
|
|
253
281
|
|
|
254
282
|
section_ids=()
|
|
255
283
|
for section in "${section_names[@]}"; do
|
|
256
|
-
section_id="$(create_notion_page "$common_page_id" "$section")"
|
|
284
|
+
section_id="$(create_notion_page "page" "$common_page_id" "$section")"
|
|
257
285
|
section_ids+=("$section_id")
|
|
258
286
|
echo "Seccion modelo MVP creada ($section): $section_id"
|
|
259
287
|
done
|
|
@@ -266,6 +294,7 @@ timestamp_utc="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
|
266
294
|
echo " \"project_name\": \"$(json_escape "$PROJECT_NAME")\","
|
|
267
295
|
echo " \"workspace_name\": \"$(json_escape "$NOTION_WORKSPACE_NAME")\","
|
|
268
296
|
echo " \"created_at_utc\": \"$timestamp_utc\","
|
|
297
|
+
echo " \"notion_parent_mode\": \"$parent_mode\","
|
|
269
298
|
echo " \"notion_parent_page_id\": \"$(json_escape "$NOTION_PARENT_PAGE_ID")\","
|
|
270
299
|
echo " \"project_root_page_id\": \"$project_root_page_id\","
|
|
271
300
|
echo " \"phase_pages\": {"
|