I think the thing with scratches is, that the one from your index.json does not exist inside of search/json. Try putting all into one single file and it should work. But that is an uneducated guess only…
{{- $.Scratch.Add "index" slice -}}
{{- range .Site.Sections -}}
{{- $.Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "categories" .Params.categories "contents" .Plain "permalink" .Permalink) -}}
{{ template "subpages" . }}
{{- end -}}
{{- $.Scratch.Get "index" | jsonify -}}
{{ define "subpages" }}
{{ if .Pages }}
{{ range .Pages}}
{{ if eq .Kind "section" }}{{- .Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "categories" .Params.categories "contents" .Plain "permalink" .Permalink) -}}{{ end }}
{{ template "subpages" . }}
{{ end }}
{{ end }}
{{ end }}
Now the index.json is not empty anymore BUT only the first level sections are included in the index file. I think the problem is the .Scatch.Add part inside the range. I have to improve the scoping somehow.
Note: I am not sure what is going on there, what do you want to list? all pages? use .Site.Pages and range one time through. All posts (main section content)? Use .Site.RegularPages and range one time through that one. Site.Sections is a list of sections and in the second loop you try to get the pages of that section… That’s double the work for the same result (and it does not work it seems )
Try replacing .Pages in your second loop with .RegularPagesRecursive and see what’s happening. It might help if you had some kind of sample content in a repo somewhere so we can wrap our heads around this. If I get it right what you want is basically like a directory of contents recursive through all levels, like in a scientific book with differing levels of depth.